Sfoglia il codice sorgente

changes for the video-generator

VISHAL BHANUSHALI 3 mesi fa
parent
commit
f85265d37b
55 ha cambiato i file con 806 aggiunte e 309 eliminazioni
  1. 3 1
      content_quality_tool/settings.py
  2. 4 2
      content_quality_tool/urls.py
  3. 8 0
      content_quality_tool_public/templates/footer.html
  4. 6 104
      content_quality_tool_public/templates/get-data.html
  5. 66 0
      content_quality_tool_public/templates/header.html
  6. 39 202
      content_quality_tool_public/templates/index.html
  7. 32 0
      content_quality_tool_public/templates/sidebar.html
  8. BIN
      media/uploads/wallet.jpg
  9. BIN
      media/uploads/wallet_Uk6Hy0q.jpg
  10. BIN
      media/uploads/wallet_k4xV4eu.jpg
  11. BIN
      media/uploads/wallet_oqDJD4Z.jpg
  12. BIN
      media/uploads/wallet_pdWTLhA.jpg
  13. BIN
      media/uploads/wallet_sz9Dovt.jpg
  14. 0 0
      video_generator/__init__.py
  15. 3 0
      video_generator/admin.py
  16. 6 0
      video_generator/apps.py
  17. 10 0
      video_generator/decorators.py
  18. 0 0
      video_generator/migrations/__init__.py
  19. 3 0
      video_generator/models.py
  20. 378 0
      video_generator/static/css/upload.css
  21. BIN
      video_generator/static/fonts/aptos-font/Aptos.eot
  22. BIN
      video_generator/static/fonts/aptos-font/Aptos.woff
  23. BIN
      video_generator/static/fonts/aptos-font/Aptos.woff2
  24. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-black-italic.ttf
  25. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-black.ttf
  26. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-bold.ttf
  27. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-extrabold-italic 2.ttf
  28. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-extrabold-italic.ttf
  29. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-extrabold.ttf
  30. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-italic.ttf
  31. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-light-italic.ttf
  32. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-light.ttf
  33. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos-semibold.ttf
  34. BIN
      video_generator/static/fonts/aptos-font/__MACOSX/._aptos.ttf
  35. BIN
      video_generator/static/fonts/aptos-font/aptos-black-italic.ttf
  36. BIN
      video_generator/static/fonts/aptos-font/aptos-black.ttf
  37. BIN
      video_generator/static/fonts/aptos-font/aptos-bold.ttf
  38. BIN
      video_generator/static/fonts/aptos-font/aptos-extrabold-italic 2.ttf
  39. BIN
      video_generator/static/fonts/aptos-font/aptos-extrabold-italic.ttf
  40. BIN
      video_generator/static/fonts/aptos-font/aptos-extrabold.ttf
  41. BIN
      video_generator/static/fonts/aptos-font/aptos-italic.ttf
  42. BIN
      video_generator/static/fonts/aptos-font/aptos-light-italic.ttf
  43. BIN
      video_generator/static/fonts/aptos-font/aptos-light.ttf
  44. BIN
      video_generator/static/fonts/aptos-font/aptos-semibold.ttf
  45. BIN
      video_generator/static/fonts/aptos-font/aptos.ttf
  46. BIN
      video_generator/static/images/45.jpg
  47. BIN
      video_generator/static/images/left.png
  48. BIN
      video_generator/static/images/logo-mini.png
  49. BIN
      video_generator/static/images/logo.png
  50. BIN
      video_generator/static/images/user2-160x160.jpg
  51. 62 0
      video_generator/static/js/upload.js
  52. 78 0
      video_generator/templates/img-upload.html
  53. 3 0
      video_generator/tests.py
  54. 13 0
      video_generator/urls.py
  55. 92 0
      video_generator/views.py

+ 3 - 1
content_quality_tool/settings.py

@@ -16,6 +16,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
 # SECURITY WARNING: keep the secret key used in production secret!
 SECRET_KEY = 'django-insecure-$6far8v=798or1wru24=zq&k*9&frm+dk%c!*w!a4wfb#z1_+3'
 # SECURITY WARNING: don't run with debug turned on in production!
+MINIMAX_API_KEY = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiJBYmh5dWRheSBBbmR1Z3VsYSIsIlVzZXJOYW1lIjoiQWJoeXVkYXkgQW5kdWd1bGEiLCJBY2NvdW50IjoiIiwiU3ViamVjdElEIjoiMTk3NjIwMTUxODMxOTE0OTMxMyIsIlBob25lIjoiIiwiR3JvdXBJRCI6IjE5NzYyMDE1MTgzMTA3NTY3NzUiLCJQYWdlTmFtZSI6IiIsIk1haWwiOiJhYmh5dWRheWFuZHVndWxhMjEzQGdtYWlsLmNvbSIsIkNyZWF0ZVRpbWUiOiIyMDI1LTEwLTA5IDIxOjAyOjE0IiwiVG9rZW5UeXBlIjoxLCJpc3MiOiJtaW5pbWF4In0.Dw6ug5FCzz_E0MOoBQ4fNN-ksaCJrwfscP5_fpmL7nxhlPJraoDHDFiznoqd5skLtIOKgsLsMNJMcutXyKTBxqqaKVGgKesZ_0JU8bAPwuPqe0MJ7ko3sZ0i858lFIqi8vH2rfgwqvvu9np2a2pQh2zvD-7LAZG4xCwcMHY7_19037s5EgONPYP7Lc_5caiF4DmNR4u7U4cunFfwGEGydpNSBP2xlk_dCRblsRNZFp-l9IkXtgi9EGOCzophhJvP8YiTMVZ2vtitt4c7YACqxtAjXT9774p299CqHAuuHcEv3MXiv0f1Zp4ERkfbsjYawYIewlfPgyw3bmFdm1py_Q'
 DEBUG = True
 ALLOWED_HOSTS = ['*', '172.29.7.103']
 # Application definition
@@ -43,7 +44,7 @@ ROOT_URLCONF = 'content_quality_tool.urls'
 TEMPLATES = [
     {
         'BACKEND': 'django.template.backends.django.DjangoTemplates',
-        'DIRS': [BASE_DIR / "templates", BASE_DIR/ 'content_quality_tool_public/templates'],
+        'DIRS': [BASE_DIR / "templates", BASE_DIR/ 'content_quality_tool_public/templates', BASE_DIR/ 'video_generator/templates'],
         'APP_DIRS': True,
         'OPTIONS': {
             'context_processors': [
@@ -90,6 +91,7 @@ USE_TZ = True
 STATIC_URL = 'static/'
 STATICFILES_DIRS = [
     os.path.join(BASE_DIR, "content_quality_tool_public/static"),
+    os.path.join(BASE_DIR, "video_generator/static")
 ]
 # Default primary key field type
 # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field

+ 4 - 2
content_quality_tool/urls.py

@@ -17,13 +17,15 @@ Including another URLconf
 from django.contrib import admin
 from django.urls import path
 from django.urls import path, include
-from content_quality_tool_public import views
+from content_quality_tool_public import views as content_views
+from video_generator import views as video_views
 # from template import views
 
 urlpatterns = [
     path('admin/', admin.site.urls),
-    path("", views.login_view, name="login_view"),
+    path("", content_views.login_view, name="login_view"),
     path('', include('content_quality_tool_public.urls')),  # Your app's routes
+    path('video/', include('video_generator.urls')),  # Your app's routes
 
     # api url
     path("core/", include("core.urls")),

+ 8 - 0
content_quality_tool_public/templates/footer.html

@@ -0,0 +1,8 @@
+<footer class="app-footer">
+    <strong>
+        Copyright &copy; 2014-2025&nbsp;
+        <a href="https://www.luminadatamatics.com/" target="_blank" class="text-decoration-none">Lumina
+            Datamatics LTD</a>.
+    </strong>
+    All rights reserved.
+</footer> 

+ 6 - 104
content_quality_tool_public/templates/get-data.html

@@ -4,14 +4,9 @@
 
 <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-    <title>Dashboard</title><!--begin::Primary Meta Tags-->
+    <title>Tool Check</title><!--begin::Primary Meta Tags-->
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="title" content="AdminLTE | Dashboard v2">
-    <meta name="author" content="ColorlibHQ">
-    <meta name="description"
-        content="AdminLTE is a Free Bootstrap 5 Admin Dashboard, 30 example pages using Vanilla JS.">
-    <meta name="keywords"
-        content="bootstrap 5, bootstrap, bootstrap 5 admin dashboard, bootstrap 5 dashboard, bootstrap 5 charts, bootstrap 5 calendar, bootstrap 5 datepicker, bootstrap 5 tables, bootstrap 5 datatable, vanilla js datatable, colorlibhq, colorlibhq dashboard, colorlibhq admin dashboard">
+    <meta name="title" content="CQT | GET DATA">\
     <!--end::Primary Meta Tags--><!--begin::Fonts-->
     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
         integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" crossorigin="anonymous">
@@ -166,91 +161,8 @@
 
 <body class="layout-fixed sidebar-expand-lg sidebar-mini app-loaded sidebar-collapse"> <!--begin::App Wrapper-->
     <div class="app-wrapper"> <!--begin::Header-->
-        <nav class="app-header navbar navbar-expand bg-body"> <!--begin::Container-->
-            <div class="container-fluid"> <!--begin::Start Navbar Links-->
-                <ul class="navbar-nav">
-                    <li class="nav-item"> <a class="nav-link" data-lte-toggle="sidebar" href="#" role="button"> <i
-                                class="bi bi-list"></i> </a> </li>
-                    <li class="nav-item d-none d-md-block"> <a  href="{% url 'file-upload' %}" class="nav-link">Home</a> </li>
-                    <!-- <li class="nav-item d-none d-md-block"> <a href="#" class="nav-link">Contact</a> </li> -->
-                </ul> <!--end::Start Navbar Links--> <!--begin::End Navbar Links-->
-                <ul class="navbar-nav ms-auto"> <!--begin::Navbar Search-->
-                    <!-- <li class="nav-item"> <a class="nav-link" data-widget="navbar-search" href="search.html"
-                            role="button"> <i class="bi bi-search"></i> </a> </li> 
-
-                    <li class="nav-item dropdown"> <a class="nav-link" data-bs-toggle="dropdown" href="#"> <i
-                                class="bi bi-bell-fill"></i> <span class="navbar-badge badge text-bg-warning">15</span>
-                        </a>
-                        <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end"> <span
-                                class="dropdown-item dropdown-header">3 Notifications</span>
-                            <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i
-                                    class="bi bi-people-fill me-2"></i> filename_22_09_2024.xml is ready for review.
-                                <span class="float-end text-secondary fs-7">1 hours</span> </a>
-                            <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i
-                                    class="bi bi-people-fill me-2"></i> filename_22_09_2024.xml is uploaded
-                                successfully.
-                                <span class="float-end text-secondary fs-7">12 hours</span> </a>
-                            <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i
-                                    class="bi bi-file-earmark-fill me-2"></i> filename_12_09_2024.xml is uploaded
-                                successfully.
-                                <span class="float-end text-secondary fs-7">2 days</span> </a>
-                            <div class="dropdown-divider"></div> <a href="#" class="dropdown-item dropdown-footer">
-                                See All Notifications
-                            </a>
-                        </div>
-                    </li>  -->
-                    <li class="nav-item"> <a class="nav-link" href="#" data-lte-toggle="fullscreen"> <i
-                                data-lte-icon="maximize" class="bi bi-arrows-fullscreen"></i> <i
-                                data-lte-icon="minimize" class="bi bi-fullscreen-exit" style="display: none;"></i> </a>
-                    </li> <!--end::Fullscreen Toggle--> <!--begin::User Menu Dropdown-->
-                    <li class="nav-item dropdown user-menu"> <a href="#" class="nav-link dropdown-toggle"
-                            data-bs-toggle="dropdown"> <img src="{% static './images/user2-160x160.jpg' %}"
-                                class="user-image rounded-circle shadow" alt="User Image"> <span
-                                class="d-none d-md-inline">{{ request.session.user_email }}</span> </a>
-                        <ul class="dropdown-menu dropdown-menu-lg dropdown-menu-end"> <!--begin::User Image-->
-                            <li class="user-header text-bg-secondary"> <img src="{% static './images/user2-160x160.jpg' %}"
-                                    class="rounded-circle shadow" alt="User Image">
-                                <p>
-                                    {{ request.session.user_email }} - Admin
-                                    <!-- <small>Since Nov. 2023</small> -->
-                                </p>
-                            </li> <!--end::User Image--> <!--begin::Menu Body-->
-
-                            <li class="user-footer"> <a href="#" class="btn btn-default btn-flat">Profile</a> <a
-                                    href="{% url 'logout' %}" class="btn btn-default btn-flat float-end">Sign out</a> </li>
-                            <!--end::Menu Footer-->
-                        </ul>
-                    </li> <!--end::User Menu Dropdown-->
-                </ul> <!--end::End Navbar Links-->
-            </div> <!--end::Container-->
-        </nav> <!--end::Header--> <!--begin::Sidebar-->
-        <aside class="app-sidebar shadow"> <!--begin::Sidebar Brand-->
-            <div class="sidebar-brand"> <!--begin::Brand Link--> <a href="{% url 'file-upload' %}" class="brand-link">
-                    <!--begin::Brand Image--> <img src="{% static './images/logo-mini.png' %}" alt="Lumina Datamatics"
-                        class="brand-image logo-mini"> <!--end::Brand Image--> <!--begin::Brand Text--> <span
-                        class="brand-text fw-light"><img style="position:relative; left: -40px;"
-                            src="{% static './images/logo.png' %}" alt="Lumina Datamatics" class="brand-image"></span>
-                    <!--end::Brand Text--> </a>
-            </div> <!--end::Sidebar Brand--> <!--begin::Sidebar Wrapper-->
-            <div class="sidebar-wrapper">
-                <nav class="mt-2"> <!--begin::Sidebar Menu-->
-                    <ul class="nav sidebar-menu flex-column" data-lte-toggle="treeview" role="menu"
-                        data-accordion="false">
-
-                        <li class="nav-item"> <a href="{% url 'file-upload' %}" class="nav-link"> <i
-                                    class="nav-icon bi bi-upload"></i>
-                                <p>Upload</p>
-                            </a> </li>
-                        <li class="nav-item"> <a href="{% url 'tool-check' %}" class="nav-link active"> <i
-                                    class="nav-icon bi bi-house"></i>
-                                <p>Home</p>
-                            </a> </li>    
-                        
-
-                    </ul> <!--end::Sidebar Menu-->
-                </nav>
-            </div> <!--end::Sidebar Wrapper-->
-        </aside> <!--end::Sidebar--> <!--begin::App Main-->
+        {% include 'header.html' %}
+        {% include 'sidebar.html' %}
         <main class="app-main"> <!--begin::App Content Header-->
             <div class="app-content-header"> <!--begin::Container-->
                 <div class="container-fluid"> <!--begin::Row-->
@@ -260,7 +172,7 @@
                         </div>
                         <div class="col-sm-6">
                             <ol class="breadcrumb float-sm-end">
-                                <li class="breadcrumb-item"><a href="./upload.html">Upload</a></li>
+                                <li class="breadcrumb-item"><a href="{% url 'tool-check' %}">Home</a></li>
                                 <li class="breadcrumb-item active" aria-current="page">
                                     Dashboard
                                 </li>
@@ -295,17 +207,7 @@
 
             </div> <!--end::Container-->
         </main> <!--end::App Main--> <!--begin::Footer-->
-        <footer class="app-footer"> <!--begin::To the end-->
-            <!-- <div class="float-end d-none d-sm-inline">Anything you want</div>--> <!--end::To the end-->
-            <!--begin::Copyright--> <strong>
-                Copyright &copy; 2014-2025&nbsp;
-                <a href="https://www.luminadatamatics.com/" target="_blank" class="text-decoration-none">Lumina
-                    Datamatics
-                    LTD</a>.
-            </strong>
-            All rights reserved.
-            <!--end::Copyright-->
-        </footer> <!--end::Footer-->
+        {% include 'footer.html' %}
     </div> <!--end::App Wrapper--> <!--begin::Script--> <!--begin::Third Party Plugin(OverlayScrollbars)-->
     <script src="https://code.jquery.com/jquery-3.7.1.min.js"
         integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>

+ 66 - 0
content_quality_tool_public/templates/header.html

@@ -0,0 +1,66 @@
+{% load static %}
+<style>
+    .circle-user-big{
+        display: inline-flex;
+        justify-content: center;
+        align-items: center;
+        background-color: #007bff;
+        color: white;
+        font-size: 66px;
+        width: 100px;  /* You can adjust this as needed */
+        height: 100px; /* You should set width and height to the same value */
+        border-radius: 50%;  /* Makes it a circle */
+        text-align: center;  /* Centers the text if it's multi-line */
+    }
+</style>
+
+<nav class="app-header navbar navbar-expand bg-body"> <!--begin::Container-->
+    <div class="container-fluid"> <!--begin::Start Navbar Links-->
+        <ul class="navbar-nav">
+            <li class="nav-item"> <a class="nav-link" data-lte-toggle="sidebar" href="#" role="button"> <i
+                        class="bi bi-list"></i> </a> </li>
+            <!-- <li class="nav-item d-none d-md-block"> <a  href="{% url 'file-upload' %}" class="nav-link">File Upload</a> 
+            </li>
+            <li class="nav-item d-none d-md-block"> <a  href="{% url 'tool-check' %}" class="nav-link">Home</a> 
+            </li> -->
+        </ul> <!--end::Start Navbar Links--> <!--begin::End Navbar Links-->
+        <ul class="navbar-nav ms-auto"> <!--begin::Navbar Search-->
+            <li class="nav-item"> <a class="nav-link" href="#" data-lte-toggle="fullscreen"> <i
+                        data-lte-icon="maximize" class="bi bi-arrows-fullscreen"></i> <i
+                        data-lte-icon="minimize" class="bi bi-fullscreen-exit" style="display: none;"></i> </a>
+            </li> <!--end::Fullscreen Toggle--> <!--begin::User Menu Dropdown-->
+            <li class="nav-item dropdown user-menu"> <a href="#" class="nav-link dropdown-toggle"
+                    data-bs-toggle="dropdown"> 
+                            {% if request.session.user_email %}
+                                <span class="user-image rounded-circle shadow" class="user-image rounded-circle shadow" style="display: inline-flex; justify-content: center; align-items: center; background-color: #007bff; color: white; font-size: 20px;">
+                                    {{ request.session.user_email|slice:":1"|upper }}
+                                </span>
+                            {% else %}
+                                <img src="{% static 'dist/img/user2-160x160.jpg' %}" class="user-image rounded-circle shadow" alt="User Image">
+                            {% endif %}
+                        <span
+                        class="d-none d-md-inline">{{ request.session.user_email }}</span> </a>
+                <ul class="dropdown-menu dropdown-menu-lg dropdown-menu-end"> <!--begin::User Image-->
+                    <li class="user-header text-bg-secondary">
+                            {% if request.session.user_email %}
+                                <span class="rounded-circle shadow circle-user-big" >
+                                    {{ request.session.user_email|slice:":1"|upper }}
+                                </span>
+                            {% else %}
+                                <img src="{% static 'dist/img/user2-160x160.jpg' %}" class="rounded-circle shadow" alt="User Image">
+                            {% endif %} 
+                        <p>
+                            {{ request.session.user_email }} - Admin
+                            <!-- <small>Since Nov. 2023</small> -->
+                        </p>
+                    </li> <!--end::User Image--> <!--begin::Menu Body-->
+
+                    <li class="user-footer"> <a href="#" class="btn btn-default btn-flat">Profile</a> <a
+                            href="{% url 'logout' %}" class="btn btn-default btn-flat float-end">Sign out</a> </li>
+                    <!--end::Menu Footer-->
+                </ul>
+            </li> <!--end::User Menu Dropdown-->
+        </ul> <!--end::End Navbar Links-->
+    </div> <!--end::Container-->
+</nav> <!--end::Header--> <!--begin::Sidebar-->
+        

+ 39 - 202
content_quality_tool_public/templates/index.html

@@ -6,13 +6,7 @@
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
     <title>Upload</title><!--begin::Primary Meta Tags-->
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="title" content="AdminLTE | Dashboard v2">
-    <meta name="author" content="ColorlibHQ">
-    <meta name="description"
-        content="AdminLTE is a Free Bootstrap 5 Admin Dashboard, 30 example pages using Vanilla JS.">
-    <meta name="keywords"
-        content="bootstrap 5, bootstrap, bootstrap 5 admin dashboard, bootstrap 5 dashboard, bootstrap 5 charts, bootstrap 5 calendar, bootstrap 5 datepicker, bootstrap 5 tables, bootstrap 5 datatable, vanilla js datatable, colorlibhq, colorlibhq dashboard, colorlibhq admin dashboard">
-    <!--end::Primary Meta Tags--><!--begin::Fonts-->
+    <meta name="title" content="CQT | Upload">
     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
         integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" crossorigin="anonymous">
     <!--end::Fonts--><!--begin::Third Party Plugin(OverlayScrollbars)-->
@@ -34,8 +28,6 @@
             top: 3px;
             font-size: 14px;
         }
-    </style>
-    <style>
         #full-page-loader {
             position: fixed;
             top: 0;
@@ -60,90 +52,8 @@
 <body class="layout-fixed sidebar-expand-lg sidebar-mini app-loaded sidebar-collapse">
     <!--begin::App Wrapper-->
     <div class="app-wrapper"> <!--begin::Header-->
-        <nav class="app-header navbar navbar-expand bg-body"> <!--begin::Container-->
-            <div class="container-fluid"> <!--begin::Start Navbar Links-->
-                <ul class="navbar-nav">
-                    <li class="nav-item"> <a class="nav-link" data-lte-toggle="sidebar" href="#" role="button"> <i
-                                class="bi bi-list"></i> </a> </li>
-                    <li class="nav-item d-none d-md-block"> <a  href="{% url 'file-upload' %}" class="nav-link">Home</a> </li>
-                    <!-- <li class="nav-item d-none d-md-block"> <a href="#" class="nav-link">Contact</a> </li> -->
-                </ul> <!--end::Start Navbar Links--> <!--begin::End Navbar Links-->
-                <ul class="navbar-nav ms-auto"> <!--begin::Navbar Search-->
-                    <!-- <li class="nav-item"> <a class="nav-link" data-widget="navbar-search" href="search.html"
-                            role="button"> <i class="bi bi-search"></i> </a> </li> 
-
-                    <li class="nav-item dropdown"> <a class="nav-link" data-bs-toggle="dropdown" href="#"> <i
-                                class="bi bi-bell-fill"></i> <span class="navbar-badge badge text-bg-warning">15</span>
-                        </a>
-                        <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end"> <span
-                                class="dropdown-item dropdown-header">3 Notifications</span>
-                            <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i
-                                    class="bi bi-people-fill me-2"></i> filename_22_09_2024.xml is ready for review.
-                                <span class="float-end text-secondary fs-7">1 hours</span> </a>
-                            <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i
-                                    class="bi bi-people-fill me-2"></i> filename_22_09_2024.xml is uploaded
-                                successfully.
-                                <span class="float-end text-secondary fs-7">12 hours</span> </a>
-                            <div class="dropdown-divider"></div> <a href="#" class="dropdown-item"> <i
-                                    class="bi bi-file-earmark-fill me-2"></i> filename_12_09_2024.xml is uploaded
-                                successfully.
-                                <span class="float-end text-secondary fs-7">2 days</span> </a>
-                            <div class="dropdown-divider"></div> <a href="#" class="dropdown-item dropdown-footer">
-                                See All Notifications
-                            </a>
-                        </div>
-                    </li>  -->
-                    <li class="nav-item"> <a class="nav-link" href="#" data-lte-toggle="fullscreen"> <i
-                                data-lte-icon="maximize" class="bi bi-arrows-fullscreen"></i> <i
-                                data-lte-icon="minimize" class="bi bi-fullscreen-exit" style="display: none;"></i> </a>
-                    </li> <!--end::Fullscreen Toggle--> <!--begin::User Menu Dropdown-->
-                    <li class="nav-item dropdown user-menu"> <a href="#" class="nav-link dropdown-toggle"
-                            data-bs-toggle="dropdown"> <img src="{% static './images/user2-160x160.jpg' %}"
-                                class="user-image rounded-circle shadow" alt="User Image"> <span
-                                class="d-none d-md-inline">{{ request.session.user_email }}</span> </a>
-                        <ul class="dropdown-menu dropdown-menu-lg dropdown-menu-end"> <!--begin::User Image-->
-                            <li class="user-header text-bg-secondary"> <img src="{% static './images/user2-160x160.jpg' %}"
-                                    class="rounded-circle shadow" alt="User Image">
-                                <p>
-                                    {{ request.session.user_email }} - Admin
-                                    <!-- <small>Since Nov. 2023</small> -->
-                                </p>
-                            </li> <!--end::User Image--> <!--begin::Menu Body-->
-
-                            <li class="user-footer"> <a href="#" class="btn btn-default btn-flat">Profile</a> <a
-                                    href="{% url 'logout' %}" class="btn btn-default btn-flat float-end">Sign out</a> </li>
-                            <!--end::Menu Footer-->
-                        </ul>
-                    </li> <!--end::User Menu Dropdown-->
-                </ul> <!--end::End Navbar Links-->
-            </div> <!--end::Container-->
-        </nav> <!--end::Header--> <!--begin::Sidebar-->
-        <aside class="app-sidebar shadow"> <!--begin::Sidebar Brand-->
-            <div class="sidebar-brand"> <!--begin::Brand Link--> <a href="{% url 'file-upload' %}" class="brand-link">
-                    <!--begin::Brand Image--> <img src="{% static './images/logo-mini.png' %}" alt="Lumina Datamatics"
-                        class="brand-image logo-mini"> <!--end::Brand Image--> <!--begin::Brand Text--> <span
-                        class="brand-text fw-light" style="position:relative; left: -40px;"><img
-                            src="{% static './images/logo.png' %}" alt="Lumina Datamatics" class="brand-image"></span>
-                    <!--end::Brand Text--> </a>
-                <!--end::Brand Link-->
-            </div> <!--end::Sidebar Brand--> <!--begin::Sidebar Wrapper-->
-            <div class="sidebar-wrapper">
-                <nav class="mt-2"> <!--begin::Sidebar Menu-->
-                    <ul class="nav sidebar-menu flex-column" data-lte-toggle="treeview" role="menu"
-                        data-accordion="false">
-                        <li class="nav-item"> <a href="{% url 'file-upload' %}" class="nav-link active"> <i
-                                    class="nav-icon bi bi-upload"></i>
-                                <p>Upload</p>
-                            </a> </li>
-                        <li class="nav-item"> <a href="{% url 'tool-check' %}" class="nav-link"> <i
-                                    class="nav-icon bi bi-house"></i>
-                                <p>Home</p>
-                            </a> </li>    
-                        
-                    </ul> <!--end::Sidebar Menu-->
-                </nav>
-            </div> <!--end::Sidebar Wrapper-->
-        </aside> <!--end::Sidebar--> <!--begin::App Main-->
+        {% include 'header.html' %}
+        {% include 'sidebar.html' %}
         <main class="app-main"> <!--begin::App Content Header-->
             <div class="app-content-header"> <!--begin::Container-->
                 <div class="container-fluid"> <!--begin::Row-->
@@ -224,17 +134,9 @@
                 </div> <!--end::Container-->
             </div> <!--end::App Content-->
         </main> <!--end::App Main--> <!--begin::Footer-->
-        <footer class="app-footer"> <!--begin::To the end-->
-            <!-- <div class="float-end d-none d-sm-inline">Anything you want</div>--> <!--end::To the end-->
-            <!--begin::Copyright--> <strong>
-                Copyright &copy; 2014-2025&nbsp;
-                <a href="https://www.luminadatamatics.com/" target="_blank" class="text-decoration-none">Lumina
-                    Datamatics LTD</a>.
-            </strong>
-            All rights reserved.
-            <!--end::Copyright-->
-        </footer> <!--end::Footer-->
-    </div> <!--end::App Wrapper--> <!--begin::Script--> <!--begin::Third Party Plugin(OverlayScrollbars)-->
+        {% include 'footer.html' %}
+
+    </div> 
     <script src="https://code.jquery.com/jquery-3.7.1.min.js"
         integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
     <script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"
@@ -277,110 +179,30 @@
                 placeholder: 'Select Competitors'
             });
         });
-
-        //$('#myForm').on('submit', function (e) {
-        //    e.preventDefault(); //stop submit
-
-        //    if ($('#file').val() != "") {
-        //        //Check if checkbox is checked then show modal
-        //        $('#myModal').modal('show');
-        //    }
-        //});
     </script> <!--end::OverlayScrollbars Configure--> <!-- OPTIONAL SCRIPTS --> <!-- apexcharts -->
     <script src="https://cdn.jsdelivr.net/npm/apexcharts@3.37.1/dist/apexcharts.min.js"
         integrity="sha256-+vh8GkaU7C9/wbSLIcwq82tQ2wTf44aOHA8HlBMwRI8=" crossorigin="anonymous"></script>
-    
-    <!-- <script>
-    function enableSubmitButton() {
+    <script>
+        document.addEventListener('DOMContentLoaded', function () {
+        const form = document.getElementById('uploadForm');
         const fileInput = document.getElementById('fileInput');
         const submitBtn = document.getElementById('submitBtn');
+        const responseDiv = document.getElementById('responseMessage');
 
-        if (fileInput.files.length > 0) {
-            submitBtn.disabled = false;
-        } else {
-            submitBtn.disabled = true;
-        }
-    }
-    </script> -->
-
-    <script>
-document.addEventListener('DOMContentLoaded', function () {
-    const form = document.getElementById('uploadForm');
-    const fileInput = document.getElementById('fileInput');
-    const submitBtn = document.getElementById('submitBtn');
-    const responseDiv = document.getElementById('responseMessage');
-
-    // Enable submit button when file is selected
-    fileInput.addEventListener('change', function () {
-        submitBtn.disabled = fileInput.files.length === 0;
-    });
-
-    // Handle form submission
-    form.addEventListener('submit', function (e) {
-        e.preventDefault();
-        $('#full-page-loader').show();
-        // Disable button during upload
-        submitBtn.disabled = true;
-        submitBtn.textContent = 'Uploading...';
-
-        const formData = new FormData(form);
-
-        fetch('/core/api/upload-rules/', {
-            method: 'POST',
-            body: formData,
-            headers: {
-                'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
-            }
-        })
-        .then(response => response.json())
-        .then(data => {
-            if (data.success) {
-                responseDiv.innerHTML = `<div class="alert alert-success">✅ ${data.message}</div>`;
-                fileInput.value = ''; // Clear file input
-                submitBtn.disabled = true; // Keep disabled until new file selected
-                $('#full-page-loader').hide();
-            } else {
-                responseDiv.innerHTML = `<div class="alert alert-danger">❌ ${data.error}</div>`;
-                submitBtn.disabled = false;
-                $('#full-page-loader').hide();
-            }
-        })
-        .catch(error => {
-            responseDiv.innerHTML = `<div class="alert alert-danger">❌ Upload failed: ${error}</div>`;
-            submitBtn.disabled = false;
-            $('#full-page-loader').hide();
-        })
-        .finally(() => {
-            submitBtn.textContent = 'Upload';
-            $('#full-page-loader').hide();
-
-            // Remove message after 5 seconds
-            setTimeout(() => {
-                responseDiv.innerHTML = '';
-                window.location.href = "/tool-check";
-            }, 3000);
-        });
-    });
-});
-</script>
-
-    <!-- <script>
-        function enableSubmitButton() {
-            const fileInput = document.getElementById('fileInput');
-            const submitBtn = document.getElementById('submitBtn');
+        // Enable submit button when file is selected
+        fileInput.addEventListener('change', function () {
             submitBtn.disabled = fileInput.files.length === 0;
-        }
-        
-        document.addEventListener('DOMContentLoaded', function () {
-            
-            const form = document.getElementById('uploadForm');
+        });
 
-            form.addEventListener('submit', function(e) {
-            e.preventDefault(); // prevent default form submission
+        // Handle form submission
+        form.addEventListener('submit', function (e) {
+            e.preventDefault();
+            $('#full-page-loader').show();
+            // Disable button during upload
+            submitBtn.disabled = true;
+            submitBtn.textContent = 'Uploading...';
 
-            const form = e.target;
             const formData = new FormData(form);
-            const responseDiv = document.getElementById('responseMessage');
 
             fetch('/core/api/upload-rules/', {
                 method: 'POST',
@@ -393,18 +215,33 @@ document.addEventListener('DOMContentLoaded', function () {
             .then(data => {
                 if (data.success) {
                     responseDiv.innerHTML = `<div class="alert alert-success">✅ ${data.message}</div>`;
+                    fileInput.value = ''; // Clear file input
+                    submitBtn.disabled = true; // Keep disabled until new file selected
+                    $('#full-page-loader').hide();
                 } else {
                     responseDiv.innerHTML = `<div class="alert alert-danger">❌ ${data.error}</div>`;
+                    submitBtn.disabled = false;
+                    $('#full-page-loader').hide();
                 }
             })
             .catch(error => {
                 responseDiv.innerHTML = `<div class="alert alert-danger">❌ Upload failed: ${error}</div>`;
+                submitBtn.disabled = false;
+                $('#full-page-loader').hide();
+            })
+            .finally(() => {
+                submitBtn.textContent = 'Upload';
+                $('#full-page-loader').hide();
+
+                // Remove message after 5 seconds
+                setTimeout(() => {
+                    responseDiv.innerHTML = '';
+                    window.location.href = "/tool-check";
+                }, 3000);
             });
         });
-                
-        });
-
-        </script> -->
+    });
+</script>
 
 </body><!--end::Body-->
 

+ 32 - 0
content_quality_tool_public/templates/sidebar.html

@@ -0,0 +1,32 @@
+{% load static %}
+<aside class="app-sidebar shadow"> <!--begin::Sidebar Brand-->
+    <div class="sidebar-brand"> <!--begin::Brand Link--> <a href="{% url 'file-upload' %}" class="brand-link">
+            <!--begin::Brand Image--> <img src="{% static './images/logo-mini.png' %}" alt="Lumina Datamatics"
+                class="brand-image logo-mini"> <!--end::Brand Image--> <!--begin::Brand Text--> <span
+                class="brand-text fw-light"><img style="position:relative; left: -40px;"
+                    src="{% static './images/logo.png' %}" alt="Lumina Datamatics" class="brand-image"></span>
+            <!--end::Brand Text--> </a>
+    </div> <!--end::Sidebar Brand--> <!--begin::Sidebar Wrapper-->
+    <div class="sidebar-wrapper">
+        <nav class="mt-2"> <!--begin::Sidebar Menu-->
+            <ul class="nav sidebar-menu flex-column" data-lte-toggle="treeview" role="menu"
+                data-accordion="false">
+                <li class="nav-item"> <a href="{% url 'file-upload' %}" class="nav-link {% if request.path == '/home/'  %}active{% endif %}"> <i
+                            class="nav-icon bi bi-upload"></i>
+                        <p>Upload</p>
+                    </a> </li>
+                <li class="nav-item"> <a href="{% url 'tool-check' %}" class="nav-link {% if request.path == '/tool-check/' %}active{% endif %}"> <i
+                            class="nav-icon bi bi-house"></i>
+                        <p>Home</p>
+                    </a> </li>  
+                <li class="nav-item"> <a href="{% url 'generate-video' %}" class="nav-link {% if request.path == '/video/generate-video/' %}active{% endif %}"> <i
+                            class="nav-icon bi bi-cpu"></i>
+                        <p>Generate Video</p>
+                    </a> </li>    
+                
+
+            </ul> <!--end::Sidebar Menu-->
+        </nav>
+    </div> <!--end::Sidebar Wrapper-->
+</aside> <!--end::Sidebar--> <!--begin::App Main-->
+        

BIN
media/uploads/wallet.jpg


BIN
media/uploads/wallet_Uk6Hy0q.jpg


BIN
media/uploads/wallet_k4xV4eu.jpg


BIN
media/uploads/wallet_oqDJD4Z.jpg


BIN
media/uploads/wallet_pdWTLhA.jpg


BIN
media/uploads/wallet_sz9Dovt.jpg


+ 0 - 0
video_generator/__init__.py


+ 3 - 0
video_generator/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 6 - 0
video_generator/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class VideoGeneratorConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'video_generator'

+ 10 - 0
video_generator/decorators.py

@@ -0,0 +1,10 @@
+from functools import wraps
+from django.shortcuts import redirect
+
+def login_required(view_func):
+    @wraps(view_func)
+    def _wrapped_view(request, *args, **kwargs):
+        if 'user_email' not in request.session:
+            return redirect('login_view')  # Redirect to your login URL name
+        return view_func(request, *args, **kwargs)
+    return _wrapped_view

+ 0 - 0
video_generator/migrations/__init__.py


+ 3 - 0
video_generator/models.py

@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.

+ 378 - 0
video_generator/static/css/upload.css

@@ -0,0 +1,378 @@
+* { margin: 0; padding: 0; box-sizing: border-box; }
+
+body {
+    font-family: 'Inter', 'Segoe UI', sans-serif;
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    min-height: 100vh;
+    overflow-x: hidden;
+    position: relative;
+}
+
+body::before {
+    content: '';
+    position: absolute;
+    width: 500px;
+    height: 500px;
+    background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
+    border-radius: 50%;
+    top: -200px;
+    right: -200px;
+    animation: float 20s ease-in-out infinite;
+}
+
+body::after {
+    content: '';
+    position: absolute;
+    width: 400px;
+    height: 400px;
+    background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);
+    border-radius: 50%;
+    bottom: -150px;
+    left: -150px;
+    animation: float 15s ease-in-out infinite reverse;
+}
+
+@keyframes float {
+    0%, 100% { transform: translate(0, 0) rotate(0deg); }
+    33% { transform: translate(30px, -30px) rotate(5deg); }
+    66% { transform: translate(-20px, 20px) rotate(-5deg); }
+}
+
+.container {
+    position: relative;
+    z-index: 1;
+}
+
+.hero-section {
+    text-align: center;
+    padding: 60px 20px 40px;
+    animation: fadeInDown 0.8s ease-out;
+}
+
+@keyframes fadeInDown {
+    from {
+    opacity: 0;
+    transform: translateY(-30px);
+    }
+    to {
+    opacity: 1;
+    transform: translateY(0);
+    }
+}
+
+.hero-title {
+    font-size: 3.5rem;
+    font-weight: 800;
+    background: linear-gradient(135deg, #fff 0%, #f0f0f0 100%);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+    margin-bottom: 15px;
+    letter-spacing: -1px;
+}
+
+.hero-subtitle {
+    color: rgba(255,255,255,0.9);
+    font-size: 1.2rem;
+    font-weight: 300;
+    max-width: 600px;
+    margin: 0 auto;
+}
+
+.upload-container {
+    display: flex;
+    justify-content: center;
+    gap: 40px;
+    margin: 50px auto;
+    flex-wrap: wrap;
+    animation: fadeInUp 0.8s ease-out 0.2s both;
+}
+
+@keyframes fadeInUp {
+    from {
+    opacity: 0;
+    transform: translateY(30px);
+    }
+    to {
+    opacity: 1;
+    transform: translateY(0);
+    }
+}
+
+.upload-card {
+    background: rgba(255, 255, 255, 0.1);
+    backdrop-filter: blur(20px);
+    border: 2px solid rgba(255, 255, 255, 0.2);
+    border-radius: 24px;
+    width: 280px;
+    height: 320px;
+    position: relative;
+    cursor: pointer;
+    transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+    overflow: hidden;
+    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+}
+
+.upload-card::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
+    opacity: 0;
+    transition: opacity 0.4s ease;
+}
+
+.upload-card:hover {
+    transform: translateY(-10px) scale(1.02);
+    border-color: rgba(255, 255, 255, 0.4);
+    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+}
+
+.upload-card:hover::before {
+    opacity: 1;
+}
+
+.upload-card.active {
+    border-color: rgba(255, 255, 255, 0.6);
+    background: rgba(255, 255, 255, 0.15);
+}
+
+.upload-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    height: 100%;
+    padding: 20px;
+    position: relative;
+    z-index: 2;
+}
+
+.upload-icon {
+    font-size: 64px;
+    margin-bottom: 20px;
+    animation: pulse 2s ease-in-out infinite;
+    filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
+}
+
+@keyframes pulse {
+    0%, 100% { transform: scale(1); }
+    50% { transform: scale(1.05); }
+}
+
+.upload-label {
+    color: white;
+    font-size: 1.4rem;
+    font-weight: 600;
+    margin-bottom: 10px;
+    text-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+.upload-hint {
+    color: rgba(255,255,255,0.7);
+    font-size: 0.9rem;
+    text-align: center;
+}
+
+input[type="file"] {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    opacity: 0;
+    cursor: pointer;
+    top: 0;
+    left: 0;
+    z-index: 3;
+}
+
+.preview-container {
+    display: none;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    height: 100%;
+    animation: zoomIn 0.4s ease-out;
+}
+
+@keyframes zoomIn {
+    from {
+    opacity: 0;
+    transform: scale(0.8);
+    }
+    to {
+    opacity: 1;
+    transform: scale(1);
+    }
+}
+
+.preview-container.show {
+    display: flex;
+}
+
+.preview-img {
+    width: 200px;
+    height: 200px;
+    object-fit: cover;
+    border-radius: 16px;
+    border: 3px solid rgba(255,255,255,0.3);
+    box-shadow: 0 8px 24px rgba(0,0,0,0.2);
+    margin-bottom: 15px;
+}
+
+.file-name {
+    color: white;
+    font-size: 0.85rem;
+    max-width: 90%;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    text-align: center;
+    background: rgba(0,0,0,0.3);
+    padding: 8px 16px;
+    border-radius: 20px;
+    backdrop-filter: blur(10px);
+}
+
+.arrow-container {
+    animation: fadeInUp 0.8s ease-out 0.4s both;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin: 0 20px;
+}
+
+.arrow {
+    font-size: 3rem;
+    color: rgba(255,255,255,0.6);
+    animation: slideRight 1.5s ease-in-out infinite;
+}
+
+@keyframes slideRight {
+    0%, 100% { transform: translateX(0); }
+    50% { transform: translateX(10px); }
+}
+
+.action-section {
+    text-align: center;
+    animation: fadeInUp 0.8s ease-out 0.6s both;
+}
+
+.generate-btn {
+    background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+    border: none;
+    color: white;
+    font-size: 1.3rem;
+    font-weight: 700;
+    padding: 18px 60px;
+    border-radius: 50px;
+    cursor: pointer;
+    transition: all 0.3s ease;
+    box-shadow: 0 10px 40px rgba(245, 87, 108, 0.4);
+    position: relative;
+    overflow: hidden;
+}
+
+.generate-btn::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: -100%;
+    width: 100%;
+    height: 100%;
+    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
+    transition: left 0.5s ease;
+}
+
+.generate-btn:hover:not(:disabled) {
+    transform: translateY(-3px);
+    box-shadow: 0 15px 50px rgba(245, 87, 108, 0.6);
+}
+
+.generate-btn:hover:not(:disabled)::before {
+    left: 100%;
+}
+
+.generate-btn:active:not(:disabled) {
+    transform: translateY(-1px);
+}
+
+.generate-btn:disabled {
+    opacity: 0.5;
+    cursor: not-allowed;
+    box-shadow: 0 5px 20px rgba(245, 87, 108, 0.2);
+}
+
+.generate-btn.processing {
+    background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
+    animation: processingPulse 1.5s ease-in-out infinite;
+}
+
+@keyframes processingPulse {
+    0%, 100% { transform: scale(1); }
+    50% { transform: scale(1.05); }
+}
+
+.modal-content {
+    background: rgba(255, 255, 255, 0.95);
+    backdrop-filter: blur(20px);
+    border: none;
+    border-radius: 24px;
+    overflow: hidden;
+    box-shadow: 0 20px 80px rgba(0,0,0,0.3);
+}
+
+.modal-header {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    color: white;
+    border: none;
+    padding: 20px 30px;
+}
+
+.modal-title {
+    font-weight: 700;
+    font-size: 1.5rem;
+}
+
+.btn-close {
+    filter: brightness(0) invert(1);
+    opacity: 0.8;
+}
+
+.btn-close:hover {
+    opacity: 1;
+}
+
+.modal-body {
+    padding: 30px;
+}
+
+#outputVideo {
+    border-radius: 16px;
+    box-shadow: 0 8px 32px rgba(0,0,0,0.2);
+}
+
+.spinner {
+    display: inline-block;
+    width: 20px;
+    height: 20px;
+    border: 3px solid rgba(255,255,255,0.3);
+    border-radius: 50%;
+    border-top-color: white;
+    animation: spin 0.8s linear infinite;
+    margin-left: 10px;
+    vertical-align: middle;
+}
+
+@keyframes spin {
+    to { transform: rotate(360deg); }
+}
+
+@media (max-width: 768px) {
+    .hero-title { font-size: 2.5rem; }
+    .upload-container { gap: 30px; }
+    .upload-card { width: 240px; height: 280px; }
+    .arrow { display: none; }
+}

BIN
video_generator/static/fonts/aptos-font/Aptos.eot


BIN
video_generator/static/fonts/aptos-font/Aptos.woff


BIN
video_generator/static/fonts/aptos-font/Aptos.woff2


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-black-italic.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-black.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-bold.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-extrabold-italic 2.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-extrabold-italic.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-extrabold.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-italic.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-light-italic.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-light.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos-semibold.ttf


BIN
video_generator/static/fonts/aptos-font/__MACOSX/._aptos.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-black-italic.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-black.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-bold.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-extrabold-italic 2.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-extrabold-italic.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-extrabold.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-italic.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-light-italic.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-light.ttf


BIN
video_generator/static/fonts/aptos-font/aptos-semibold.ttf


BIN
video_generator/static/fonts/aptos-font/aptos.ttf


BIN
video_generator/static/images/45.jpg


BIN
video_generator/static/images/left.png


BIN
video_generator/static/images/logo-mini.png


BIN
video_generator/static/images/logo.png


BIN
video_generator/static/images/user2-160x160.jpg


+ 62 - 0
video_generator/static/js/upload.js

@@ -0,0 +1,62 @@
+let startFile = null;
+let endFile = null;
+const processBtn = document.getElementById("processBtn");
+
+document.getElementById("startFrame").addEventListener("change", (e) => handleFile(e, "start"));
+document.getElementById("endFrame").addEventListener("change", (e) => handleFile(e, "end"));
+
+function handleFile(e, type) {
+    const file = e.target.files[0];
+    if (!file) return;
+    
+    const preview = document.getElementById(type + "Preview");
+    const fileName = document.getElementById(type + "FileName");
+    const previewContainer = document.getElementById(type + "PreviewContainer");
+    const card = document.getElementById(type + "Card");
+    const uploadContent = card.querySelector('.upload-content');
+    
+    preview.src = URL.createObjectURL(file);
+    fileName.textContent = file.name;
+    
+    uploadContent.style.display = 'none';
+    previewContainer.classList.add('show');
+    card.classList.add('active');
+    
+    if (type === "start") startFile = file;
+    else endFile = file;
+    
+    processBtn.disabled = !(startFile && endFile);
+}
+
+processBtn.addEventListener("click", async () => {
+    const formData = new FormData();
+    formData.append("first_frame", startFile);
+    formData.append("last_frame", endFile);
+
+    processBtn.innerHTML = '⏳ Processing<span class="spinner"></span>';
+    processBtn.classList.add('processing');
+    processBtn.disabled = true;
+
+    try {
+    const response = await fetch("/video/video-generator/", { method: "POST", body: formData });
+    const data = await response.json();
+
+    processBtn.innerHTML = "🚀 Generate Video";
+    processBtn.classList.remove('processing');
+    processBtn.disabled = false;
+
+    if (data.video_url) {
+        const video = document.getElementById("outputVideo");
+        video.src = data.video_url;
+        new bootstrap.Modal(document.getElementById("videoModal")).show();
+    } else {
+        alert("No video URL in response. Check backend output.");
+        console.log("Response:", data);
+    }
+    } catch (err) {
+    alert("Error: " + err.message);
+    processBtn.innerHTML = "🚀 Generate Video";
+    processBtn.classList.remove('processing');
+    processBtn.disabled = false;
+    }
+});

+ 78 - 0
video_generator/templates/img-upload.html

@@ -0,0 +1,78 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>AI Image to Video Generator</title>
+  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
+  <link rel="stylesheet" href="{% static 'css/upload.css' %}">
+</head>
+<body>
+  <div class="container">
+    <div class="hero-section">
+      <h1 class="hero-title">✨ AI Video Magic</h1>
+      <p class="hero-subtitle">Transform your images into stunning transition videos with cutting-edge AI technology</p>
+    </div>
+    
+    <div class="upload-container">
+      <!-- Start Frame Card -->
+      <div class="upload-card" id="startCard">
+        <div class="upload-content">
+          <div class="upload-icon">🎬</div>
+          <div class="upload-label">Start Frame</div>
+          <div class="upload-hint">Click or drag to upload</div>
+        </div>
+        <div class="preview-container" id="startPreviewContainer">
+          <img id="startPreview" class="preview-img" alt="Start Preview">
+          <div id="startFileName" class="file-name"></div>
+        </div>
+        <input type="file" id="startFrame" accept="image/*">
+      </div>
+      
+      <div class="arrow-container">
+        <div class="arrow">→</div>
+      </div>
+      
+      <!-- End Frame Card -->
+      <div class="upload-card" id="endCard">
+        <div class="upload-content">
+          <div class="upload-icon">🎞️</div>
+          <div class="upload-label">End Frame</div>
+          <div class="upload-hint">Click or drag to upload</div>
+        </div>
+        <div class="preview-container" id="endPreviewContainer">
+          <img id="endPreview" class="preview-img" alt="End Preview">
+          <div id="endFileName" class="file-name"></div>
+        </div>
+        <input type="file" id="endFrame" accept="image/*">
+      </div>
+    </div>
+    
+    <div class="action-section">
+      <button class="generate-btn" id="processBtn" disabled>
+        🚀 Generate Video
+      </button>
+    </div>
+  </div>
+
+  <!-- Video Modal -->
+  <div class="modal fade" id="videoModal" tabindex="-1" aria-hidden="true">
+    <div class="modal-dialog modal-dialog-centered modal-lg">
+      <div class="modal-content">
+        <div class="modal-header">
+          <h5 class="modal-title">🎉 Your Video is Ready!</h5>
+          <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
+        </div>
+        <div class="modal-body">
+          <video id="outputVideo" class="w-100" controls></video>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+  <script src="{% static 'js/upload.js' %}"></script>
+</body>
+{% include 'footer.html' %}
+</html>

+ 3 - 0
video_generator/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 13 - 0
video_generator/urls.py

@@ -0,0 +1,13 @@
+from django.urls import path
+from django.conf import settings
+from . import views
+from django.conf.urls.static import static
+
+
+urlpatterns = [
+    path('generate-video/', views.img_process, name='generate-video'),
+    path('video-generator/', views.process_images, name='video-generator'),
+]
+
+if settings.DEBUG:
+    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

+ 92 - 0
video_generator/views.py

@@ -0,0 +1,92 @@
+# # Standard Library Imports
+import os
+import time
+
+# # Third-Party Library Imports
+import requests
+
+# # Django Imports
+from django.shortcuts import render
+from django.conf import settings
+from django.http import JsonResponse
+from django.views.decorators.csrf import csrf_exempt
+from django.core.files.storage import default_storage
+from django.core.files.base import ContentFile
+from .decorators import login_required
+
+@login_required
+def img_process(request):
+    return render(request, 'img-upload.html')
+
+MINIMAX_API_KEY = settings.MINIMAX_API_KEY
+VIDEO_GEN_URL = "https://api.minimax.io/v1/video_generation"
+QUERY_URL = "https://api.minimax.io/v1/query/video_generation"
+FILES_URL = "https://api.minimax.io/v1/files/retrieve"
+POLL_INTERVAL = 5  # seconds
+
+
+@csrf_exempt
+def process_images(request):
+    if request.method != "POST":
+        return JsonResponse({"error": "Invalid request method."}, status=405)
+
+    first_frame = request.FILES.get("first_frame")
+    last_frame = request.FILES.get("last_frame")
+
+    if not first_frame or not last_frame:
+        return JsonResponse({"error": "Both first_frame and last_frame are required."}, status=400)
+
+    try:
+        # --- Save uploaded images locally ---
+        first_path = default_storage.save(f"uploads/{first_frame.name}", first_frame)
+        last_path = default_storage.save(f"uploads/{last_frame.name}", last_frame)
+
+        first_filename = os.path.basename(first_path)
+        last_filename = os.path.basename(last_path)
+
+        # --- Build public URLs using ngrok ---
+        # Replace this with your current ngrok URL
+        NGROK_URL = "https://postcartilaginous-kyler-nonrun.ngrok-free.dev"
+        first_url = f"{NGROK_URL}{settings.MEDIA_URL}{first_filename}"
+        last_url = f"{NGROK_URL}{settings.MEDIA_URL}{last_filename}"
+
+        # --- Call MiniMax API ---
+        headers = {"Authorization": f"Bearer {MINIMAX_API_KEY}"}
+        payload = {
+            "prompt": "Generate a smooth transition between two frames.",
+            "first_frame_image": first_url,
+            "last_frame_image": last_url,
+            "model": "MiniMax-Hailuo-02",
+            "duration": 6,
+            "resolution": "1080P"
+        }
+
+        response = requests.post(VIDEO_GEN_URL, headers=headers, json=payload)
+        response.raise_for_status()
+        print("Response", response.json())
+        task_id = response.json().get("task_id")
+        if not task_id:
+            return JsonResponse({"error": "Failed to get task_id from MiniMax API."}, status=500)
+
+        # --- Poll MiniMax until task completes ---
+        file_id = None
+        while not file_id:
+            time.sleep(POLL_INTERVAL)
+            status_resp = requests.get(QUERY_URL, headers=headers, params={"task_id": task_id})
+            status_resp.raise_for_status()
+            status_data = status_resp.json()
+            status = status_data.get("status")
+            if status == "Success":
+                file_id = status_data.get("file_id")
+            elif status == "Fail":
+                return JsonResponse({"error": status_data.get("error_message", "Video generation failed")}, status=500)
+
+        # --- Get final video download URL ---
+        download_resp = requests.get(FILES_URL, headers=headers, params={"file_id": file_id})
+        download_resp.raise_for_status()
+        download_url = download_resp.json()["file"]["download_url"]
+
+        return JsonResponse({"video_url": download_url})
+
+    except Exception as e:
+        return JsonResponse({"error": str(e)}, status=500)