ChandimaPrabath commited on
Commit
508e2bf
·
1 Parent(s): 39624f5

0.0.3 V Alpha UI update

Browse files
frontend/package-lock.json CHANGED
@@ -18,6 +18,7 @@
18
  "http-proxy-middleware": "^2.0.6",
19
  "react": "^18.2.0",
20
  "react-dom": "^18.2.0",
 
21
  "react-router-dom": "^6.25.1",
22
  "react-scripts": "5.0.1",
23
  "serve": "^14.2.3",
@@ -8390,6 +8391,11 @@
8390
  "url": "https://github.com/sindresorhus/execa?sponsor=1"
8391
  }
8392
  },
 
 
 
 
 
8393
  "node_modules/exit": {
8394
  "version": "0.1.2",
8395
  "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@@ -15141,6 +15147,29 @@
15141
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
15142
  "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
15143
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15144
  "node_modules/react-refresh": {
15145
  "version": "0.11.0",
15146
  "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@@ -17558,6 +17587,14 @@
17558
  "makeerror": "1.0.12"
17559
  }
17560
  },
 
 
 
 
 
 
 
 
17561
  "node_modules/watchpack": {
17562
  "version": "2.4.0",
17563
  "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
 
18
  "http-proxy-middleware": "^2.0.6",
19
  "react": "^18.2.0",
20
  "react-dom": "^18.2.0",
21
+ "react-modal": "^3.16.1",
22
  "react-router-dom": "^6.25.1",
23
  "react-scripts": "5.0.1",
24
  "serve": "^14.2.3",
 
8391
  "url": "https://github.com/sindresorhus/execa?sponsor=1"
8392
  }
8393
  },
8394
+ "node_modules/exenv": {
8395
+ "version": "1.2.2",
8396
+ "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
8397
+ "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
8398
+ },
8399
  "node_modules/exit": {
8400
  "version": "0.1.2",
8401
  "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
 
15147
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
15148
  "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
15149
  },
15150
+ "node_modules/react-lifecycles-compat": {
15151
+ "version": "3.0.4",
15152
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
15153
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
15154
+ },
15155
+ "node_modules/react-modal": {
15156
+ "version": "3.16.1",
15157
+ "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
15158
+ "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==",
15159
+ "dependencies": {
15160
+ "exenv": "^1.2.0",
15161
+ "prop-types": "^15.7.2",
15162
+ "react-lifecycles-compat": "^3.0.0",
15163
+ "warning": "^4.0.3"
15164
+ },
15165
+ "engines": {
15166
+ "node": ">=8"
15167
+ },
15168
+ "peerDependencies": {
15169
+ "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18",
15170
+ "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
15171
+ }
15172
+ },
15173
  "node_modules/react-refresh": {
15174
  "version": "0.11.0",
15175
  "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
 
17587
  "makeerror": "1.0.12"
17588
  }
17589
  },
17590
+ "node_modules/warning": {
17591
+ "version": "4.0.3",
17592
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
17593
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
17594
+ "dependencies": {
17595
+ "loose-envify": "^1.0.0"
17596
+ }
17597
+ },
17598
  "node_modules/watchpack": {
17599
  "version": "2.4.0",
17600
  "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
frontend/package.json CHANGED
@@ -13,6 +13,7 @@
13
  "http-proxy-middleware": "^2.0.6",
14
  "react": "^18.2.0",
15
  "react-dom": "^18.2.0",
 
16
  "react-router-dom": "^6.25.1",
17
  "react-scripts": "5.0.1",
18
  "serve": "^14.2.3",
 
13
  "http-proxy-middleware": "^2.0.6",
14
  "react": "^18.2.0",
15
  "react-dom": "^18.2.0",
16
+ "react-modal": "^3.16.1",
17
  "react-router-dom": "^6.25.1",
18
  "react-scripts": "5.0.1",
19
  "serve": "^14.2.3",
frontend/src/App.css CHANGED
@@ -1,5 +1,5 @@
1
  body {
2
- background-color: #1a1a1a; /* Dark background for a sleek look */
3
  color: #f5f5f5; /* Light gray text for contrast */
4
  font-family: "Signika", sans-serif;
5
  font-optical-sizing: auto;
@@ -15,8 +15,8 @@ body {
15
  .App-header {
16
  background: linear-gradient(
17
  to right,
18
- #232323,
19
- #1b1b1b
20
  ); /* Dark gradient for a sophisticated header */
21
  padding: 10px 0; /* Padding for spacing */
22
  color: #f5f5f5; /* Light text color for readability */
 
1
  body {
2
+ background: linear-gradient(to top, #1e2634 30%, #181e28);
3
  color: #f5f5f5; /* Light gray text for contrast */
4
  font-family: "Signika", sans-serif;
5
  font-optical-sizing: auto;
 
15
  .App-header {
16
  background: linear-gradient(
17
  to right,
18
+ #171717,
19
+ #31506e
20
  ); /* Dark gradient for a sophisticated header */
21
  padding: 10px 0; /* Padding for spacing */
22
  color: #f5f5f5; /* Light text color for readability */
frontend/src/api/LoadBalancer.js CHANGED
@@ -1,101 +1,132 @@
1
  class LoadBalancerAPI {
2
- constructor(baseURL) {
3
- this.baseURL = baseURL;
4
- }
5
-
6
- async getInstances() {
7
- return this._getRequest('/api/instances');
8
- }
9
-
10
- async getInstancesHealth() {
11
- return this._getRequest('/api/instances/health');
12
- }
13
-
14
- async getMovieByTitle(title) {
15
- return this._getRequest(`/api/film/${encodeURIComponent(title)}`);
16
- }
17
-
18
- async getTVEpisode(title, season, episode) {
19
- return this._getRequest(`/api/tv/${encodeURIComponent(title)}/${season}/${episode}`);
20
- }
21
-
22
- async getFilmIDByTitle(title) {
23
- return this._getRequest(`/api/filmid/${encodeURIComponent(title)}`);
24
- }
25
-
26
- async getEpisodeIDByTitleSeasonEpisode(title, season, episode) {
27
- return this._getRequest(`/api/episodeid/${encodeURIComponent(title)}/${season}/${episode}`);
28
- }
29
-
30
- async getCacheSize() {
31
- return this._getRequest('/api/cache/size');
32
- }
33
-
34
- async clearCache() {
35
- return this._postRequest('/api/cache/clear');
36
- }
37
-
38
- async getTVStore() {
39
- return this._getRequest('/api/tv/store');
40
- }
41
-
42
- async getFilmStore() {
43
- return this._getRequest('/api/film/store');
44
- }
45
-
46
- async getFilmMetadataByTitle(title) {
47
- return this._getRequest(`/api/film/metadata/${encodeURIComponent(title)}`);
48
- }
49
-
50
- async getTVMetadataByTitle(title) {
51
- return this._getRequest(`/api/tv/metadata/${encodeURIComponent(title)}`);
52
- }
53
-
54
- async getAllFilms() {
55
- return this._getRequest('/api/film/all');
56
- }
57
-
58
- async getAllTVShows() {
59
- return this._getRequest('/api/tv/all');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
-
62
- // Helper methods for GET and POST requests
63
- async _getRequest(endpoint) {
64
- try {
65
- const response = await fetch(`${this.baseURL}${endpoint}`, {
66
- method: 'GET',
67
- headers: { 'Content-Type': 'application/json' },
68
- });
69
- console.log(`api endpoint: ${this.baseURL}${endpoint}`)
70
- return await this._handleResponse(response);
71
- } catch (error) {
72
- console.error(`Error during GET request to ${endpoint}:`, error);
73
- throw error;
74
- }
75
  }
76
-
77
- async _postRequest(endpoint, body) {
78
- try {
79
- const response = await fetch(`${this.baseURL}${endpoint}`, {
80
- method: 'POST',
81
- headers: { 'Content-Type': 'application/json' },
82
- body: JSON.stringify(body),
83
- });
84
- return await this._handleResponse(response);
85
- } catch (error) {
86
- console.error(`Error during POST request to ${endpoint}:`, error);
87
- throw error;
88
- }
89
  }
90
-
91
- async _handleResponse(response) {
92
- if (!response.ok) {
93
- const errorDetails = await response.text();
94
- throw new Error(`HTTP error! status: ${response.status}, details: ${errorDetails}`);
95
- }
96
- return response.json();
 
 
 
 
 
 
 
97
  }
98
  }
99
-
100
- export { LoadBalancerAPI };
101
-
 
1
  class LoadBalancerAPI {
2
+ constructor(baseURL) {
3
+ this.baseURL = baseURL;
4
+ this.filmCache = null; // Cache for film store
5
+ this.tvCache = null; // Cache for TV store
6
+ }
7
+
8
+ async getInstances() {
9
+ return this._getRequest('/api/instances');
10
+ }
11
+
12
+ async getInstancesHealth() {
13
+ return this._getRequest('/api/instances/health');
14
+ }
15
+
16
+ async getMovieByTitle(title) {
17
+ return this._getRequest(`/api/film/${encodeURIComponent(title)}`);
18
+ }
19
+
20
+ async getTVEpisode(title, season, episode) {
21
+ return this._getRequest(`/api/tv/${encodeURIComponent(title)}/${season}/${episode}`);
22
+ }
23
+
24
+ async getFilmIDByTitle(title) {
25
+ return this._getRequest(`/api/filmid/${encodeURIComponent(title)}`);
26
+ }
27
+
28
+ async getEpisodeIDByTitleSeasonEpisode(title, season, episode) {
29
+ return this._getRequest(`/api/episodeid/${encodeURIComponent(title)}/${season}/${episode}`);
30
+ }
31
+
32
+ async getCacheSize() {
33
+ return this._getRequest('/api/cache/size');
34
+ }
35
+
36
+ async clearCache() {
37
+ return this._postRequest('/api/cache/clear');
38
+ }
39
+
40
+ async getTVStore() {
41
+ const response = await this._getRequest('/api/tv/store');
42
+
43
+ if (response && Object.keys(response).length > 0) {
44
+ this.tvCache = response; // Update cache if response is not empty
45
+ }
46
+
47
+ return this.tvCache || {}; // Return cache if response is empty
48
+ }
49
+
50
+ async getFilmStore() {
51
+ const response = await this._getRequest('/api/film/store');
52
+
53
+ if (response && Object.keys(response).length > 0) {
54
+ this.filmCache = response; // Update cache if response is not empty
55
+ }
56
+
57
+ return this.filmCache || {}; // Return cache if response is empty
58
+ }
59
+
60
+ async getFilmMetadataByTitle(title) {
61
+ return this._getRequest(`/api/film/metadata/${encodeURIComponent(title)}`);
62
+ }
63
+
64
+ async getTVMetadataByTitle(title) {
65
+ return this._getRequest(`/api/tv/metadata/${encodeURIComponent(title)}`);
66
+ }
67
+
68
+ async getAllFilms() {
69
+ return this._getRequest('/api/film/all');
70
+ }
71
+
72
+ async getAllTVShows() {
73
+ return this._getRequest('/api/tv/all');
74
+ }
75
+
76
+ async getDownloadProgress(url) {
77
+ return this._getRequestNoBase(url);
78
+ }
79
+
80
+ // Helper methods for GET and POST requests
81
+ async _getRequest(endpoint) {
82
+ try {
83
+ const response = await fetch(`${this.baseURL}${endpoint}`, {
84
+ method: 'GET',
85
+ headers: { 'Content-Type': 'application/json' },
86
+ });
87
+ console.log(`api endpoint: ${this.baseURL}${endpoint}`);
88
+ return await this._handleResponse(response);
89
+ } catch (error) {
90
+ console.error(`Error during GET request to ${endpoint}:`, error);
91
+ throw error;
92
  }
93
+ }
94
+
95
+ async _postRequest(endpoint, body) {
96
+ try {
97
+ const response = await fetch(`${this.baseURL}${endpoint}`, {
98
+ method: 'POST',
99
+ headers: { 'Content-Type': 'application/json' },
100
+ body: JSON.stringify(body),
101
+ });
102
+ return await this._handleResponse(response);
103
+ } catch (error) {
104
+ console.error(`Error during POST request to ${endpoint}:`, error);
105
+ throw error;
 
106
  }
107
+ }
108
+
109
+ async _handleResponse(response) {
110
+ if (!response.ok) {
111
+ const errorDetails = await response.text();
112
+ throw new Error(`HTTP error! status: ${response.status}, details: ${errorDetails}`);
 
 
 
 
 
 
 
113
  }
114
+ return response.json();
115
+ }
116
+
117
+ async _getRequestNoBase(url) {
118
+ try {
119
+ const response = await fetch(`${url}`, {
120
+ method: 'GET',
121
+ headers: { 'Content-Type': 'application/json' },
122
+ });
123
+ console.log(`api endpoint: ${url}`);
124
+ return await this._handleResponse(response);
125
+ } catch (error) {
126
+ console.error(`Error during GET request to ${url}:`, error);
127
+ throw error;
128
  }
129
  }
130
+ }
131
+
132
+ export { LoadBalancerAPI };
frontend/src/components/film/modals/playModal.css ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .play-modal {
2
+ position: absolute;
3
+ top: 50%;
4
+ left: 50%;
5
+ transform: translate(-50%, -50%);
6
+ background: linear-gradient(135deg, #171717, #31506e); /* Deep blue gradient */
7
+ padding: 30px;
8
+ border-radius: 20px;
9
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
10
+ color: #f0f0f0; /* Light gray text color */
11
+ text-align: center;
12
+ width: 90%;
13
+ max-width: 600px;
14
+ z-index: 1001;
15
+ animation: fadeIn 0.3s ease-out;
16
+ }
17
+
18
+ .play-modal-overlay {
19
+ background-color: rgba(0, 0, 0, 0.8);
20
+ z-index: 1000;
21
+ }
22
+
23
+ .modal-content {
24
+ display: flex;
25
+ flex-direction: column;
26
+ align-items: center;
27
+ }
28
+
29
+ .modal-title {
30
+ font-size: 2.5rem;
31
+ margin-bottom: 20px;
32
+ color: #ffffff; /* White text color for the title */
33
+ font-family: 'Roboto', sans-serif;
34
+ text-transform: uppercase;
35
+ letter-spacing: 2px;
36
+ }
37
+
38
+ .progress-container {
39
+ width: 100%;
40
+ }
41
+
42
+ .progress-bar {
43
+ width: 100%;
44
+ background: #003366; /* Darker blue for the progress bar background */
45
+ border-radius: 10px;
46
+ overflow: hidden;
47
+ margin-bottom: 10px;
48
+ height: 15px;
49
+ }
50
+
51
+ .progress {
52
+ height: 100%;
53
+ background: #1e90ff; /* Bright blue for the progress */
54
+ transition: width 0.3s ease-in-out;
55
+ }
56
+
57
+ .progress-info {
58
+ display: flex;
59
+ justify-content: space-between;
60
+ width: 100%;
61
+ padding: 0 10px;
62
+ }
63
+
64
+ .progress-text {
65
+ font-size: 1.1rem;
66
+ color: #d3d3d3; /* Light gray text color for progress info */
67
+ }
68
+
69
+ .modal-actions {
70
+ display: flex;
71
+ justify-content: center;
72
+ gap: 15px;
73
+ }
74
+
75
+ .confirm-button {
76
+ background: #3f75d2; /* Soft blue for the confirm button */
77
+ color: #fff;
78
+ border: none;
79
+ padding: 12px 25px;
80
+ border-radius: 8px;
81
+ cursor: pointer;
82
+ font-size: 1.1rem;
83
+ transition: background 0.3s ease;
84
+ }
85
+
86
+ .confirm-button:hover {
87
+ background: #4244b3; /* Slightly darker blue on hover */
88
+ }
89
+
90
+ .cancel-button {
91
+ background: #b0bec5; /* Light silver-blue for the cancel button */
92
+ color: #000; /* Dark text color for contrast */
93
+ border: none;
94
+ padding: 12px 25px;
95
+ border-radius: 8px;
96
+ cursor: pointer;
97
+ font-size: 1.1rem;
98
+ transition: background 0.3s ease;
99
+ }
100
+
101
+ .cancel-button:hover {
102
+ background: #90a4ae; /* Slightly darker silver-blue on hover */
103
+ }
104
+
105
+ @keyframes fadeIn {
106
+ from {
107
+ opacity: 0;
108
+ }
109
+ to {
110
+ opacity: 1;
111
+ }
112
+ }
113
+
114
+ @media (max-width: 768px) {
115
+ .play-modal {
116
+ width: 95%;
117
+ padding: 20px;
118
+ }
119
+
120
+ .modal-title {
121
+ font-size: 2rem;
122
+ }
123
+
124
+ .confirm-button, .cancel-button {
125
+ padding: 10px 20px;
126
+ font-size: 1rem;
127
+ }
128
+ }
129
+
frontend/src/components/film/modals/playModal.js ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from "react";
2
+ import Modal from "react-modal";
3
+ import apiClient from "../../../api/apiClient";
4
+ import "./playModal.css";
5
+
6
+ Modal.setAppElement("#root");
7
+
8
+ const PlayModal = ({ isOpen, onRequestClose, videoUrl, setVideoUrl, downloadProgress, title }) => {
9
+ const [progress, setProgress] = useState(null);
10
+ const [completed, setCompleted] = useState(false);
11
+
12
+ useEffect(() => {
13
+ let interval;
14
+ if (downloadProgress) {
15
+ interval = setInterval(async () => {
16
+ try {
17
+ const response = await apiClient.getDownloadProgress(downloadProgress);
18
+ setProgress(response.progress);
19
+ if (response.progress.status === "Completed") {
20
+ setCompleted(true);
21
+ clearInterval(interval);
22
+ }
23
+ } catch (error) {
24
+ console.error("Failed to fetch download progress:", error);
25
+ }
26
+ }, 5000);
27
+ }
28
+ return () => clearInterval(interval);
29
+ }, [downloadProgress]);
30
+
31
+ useEffect(() => {
32
+ const fetchVideoUrl = async () => {
33
+ try {
34
+ const response = await apiClient.getMovieByTitle(title);
35
+ if (response.url) {
36
+ setVideoUrl(response.url);
37
+ }
38
+ } catch (error) {
39
+ console.error("Failed to fetch video URL:", error);
40
+ }
41
+ };
42
+
43
+ if (completed) {
44
+ const timer = setTimeout(() => {
45
+ fetchVideoUrl();
46
+ }, 5000);
47
+ return () => clearTimeout(timer);
48
+ }
49
+ }, [completed, title, setVideoUrl]);
50
+
51
+ const handleConfirm = () => {
52
+ if (videoUrl) {
53
+ window.location.href = videoUrl;
54
+ }
55
+ };
56
+
57
+ const handleCancel = () => {
58
+ onRequestClose();
59
+ };
60
+
61
+ return (
62
+ <Modal
63
+ isOpen={isOpen}
64
+ onRequestClose={onRequestClose}
65
+ contentLabel="Play Modal"
66
+ className="play-modal"
67
+ overlayClassName="play-modal-overlay"
68
+ >
69
+ <div className="modal-content">
70
+ {videoUrl ? (
71
+ <>
72
+ <h2 className="modal-title">Ready to Play</h2>
73
+ <div className="modal-actions">
74
+ <button className="confirm-button" onClick={handleConfirm}>Play</button>
75
+ <button className="cancel-button" onClick={handleCancel}>No</button>
76
+ </div>
77
+ </>
78
+ ) : progress ? (
79
+ <>
80
+ <h2 className="modal-title">Downloading...</h2>
81
+ <div className="progress-container">
82
+ <div className="progress-bar">
83
+ <div className="progress" style={{ width: `${progress.progress}%` }}></div>
84
+ </div>
85
+ <div className="progress-info">
86
+ <span className="progress-text">Progress: {progress.progress.toFixed(2)}%</span>
87
+ <span className="progress-text">ETA: {progress.eta.toFixed(2)} seconds</span>
88
+ </div>
89
+ </div>
90
+ </>
91
+ ) : (
92
+ <h2 className="modal-title">Initializing download...</h2>
93
+ )}
94
+ </div>
95
+ </Modal>
96
+ );
97
+ };
98
+
99
+ export default PlayModal;
frontend/src/config.js CHANGED
@@ -1,7 +1,7 @@
1
  // src/config.js
2
  const config = {
3
  apiBaseUrl: 'https://unicone-studio-load-balancer.hf.space',
4
- version: "0.0.2 V Alpha",
5
  };
6
 
7
  export default config;
 
1
  // src/config.js
2
  const config = {
3
  apiBaseUrl: 'https://unicone-studio-load-balancer.hf.space',
4
+ version: "0.0.3 V Alpha",
5
  };
6
 
7
  export default config;
frontend/src/pages/filmDetailsPage.css CHANGED
@@ -40,7 +40,7 @@
40
  left: 0;
41
  right: 0;
42
  bottom: 0;
43
- background: linear-gradient(to top, rgb(24, 24, 24) 30%, transparent);
44
  z-index: 1;
45
  }
46
 
 
40
  left: 0;
41
  right: 0;
42
  bottom: 0;
43
+ background: linear-gradient(to top, #181e28 30%, transparent);
44
  z-index: 1;
45
  }
46
 
frontend/src/pages/filmDetailsPage.js CHANGED
@@ -1,8 +1,9 @@
1
- import React, { useEffect, useState } from "react";
2
  import { useParams } from "react-router-dom";
3
  import apiClient from "../api/apiClient";
4
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5
  import { faPlay } from "@fortawesome/free-solid-svg-icons";
 
6
  import "./filmDetailsPage.css";
7
 
8
  function FilmDetailsPage() {
@@ -10,28 +11,56 @@ function FilmDetailsPage() {
10
  const [filmDetails, setFilmDetails] = useState(null);
11
  const [loading, setLoading] = useState(true);
12
  const [error, setError] = useState(null);
 
 
 
13
 
14
- useEffect(() => {
15
- const fetchFilmDetails = async () => {
16
- try {
17
- const response = await apiClient.getFilmMetadataByTitle(title);
18
- if (response.data) {
19
- setFilmDetails(response.data);
20
- } else {
21
- setError("No film details found.");
22
- }
23
- } catch (err) {
24
- setError("Failed to fetch film details.");
25
- } finally {
26
- setLoading(false);
27
  }
28
- };
 
 
 
 
 
29
 
30
- fetchFilmDetails();
 
 
 
 
 
 
 
 
 
 
 
 
31
  }, [title]);
32
 
 
 
 
 
 
 
 
 
 
 
33
  const handlePlay = () => {
34
- window.location.href = `/player/${title}`;
 
 
 
 
35
  };
36
 
37
  if (loading) {
@@ -147,6 +176,14 @@ function FilmDetailsPage() {
147
  </div>
148
  )}
149
  </div>
 
 
 
 
 
 
 
 
150
  </div>
151
  );
152
  }
 
1
+ import React, { useEffect, useState, useCallback } from "react";
2
  import { useParams } from "react-router-dom";
3
  import apiClient from "../api/apiClient";
4
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5
  import { faPlay } from "@fortawesome/free-solid-svg-icons";
6
+ import PlayModal from "../components/film/modals/playModal";
7
  import "./filmDetailsPage.css";
8
 
9
  function FilmDetailsPage() {
 
11
  const [filmDetails, setFilmDetails] = useState(null);
12
  const [loading, setLoading] = useState(true);
13
  const [error, setError] = useState(null);
14
+ const [isModalOpen, setIsModalOpen] = useState(false);
15
+ const [videoUrl, setVideoUrl] = useState(null);
16
+ const [downloadProgress, setDownloadProgress] = useState(null);
17
 
18
+ const fetchFilmDetails = useCallback(async () => {
19
+ try {
20
+ const response = await apiClient.getFilmMetadataByTitle(title);
21
+ if (response.data) {
22
+ setFilmDetails(response.data);
23
+ } else {
24
+ setError("No film details found.");
 
 
 
 
 
 
25
  }
26
+ } catch (err) {
27
+ setError("Failed to fetch film details.");
28
+ } finally {
29
+ setLoading(false);
30
+ }
31
+ }, [title]);
32
 
33
+ const fetchVideoUrl = useCallback(async () => {
34
+ try {
35
+ const response = await apiClient.getMovieByTitle(title);
36
+ console.log(response);
37
+ if (response.url) {
38
+ setVideoUrl(response.url);
39
+ } else if (response.progress_url) {
40
+ setDownloadProgress(response.progress_url);
41
+ }
42
+ } catch (err) {
43
+ console.log(err);
44
+ setError("Failed to fetch video URL.");
45
+ }
46
  }, [title]);
47
 
48
+ useEffect(() => {
49
+ fetchFilmDetails();
50
+ }, [fetchFilmDetails]);
51
+
52
+ useEffect(() => {
53
+ if (isModalOpen && !videoUrl) {
54
+ fetchVideoUrl();
55
+ }
56
+ }, [isModalOpen, videoUrl, fetchVideoUrl]);
57
+
58
  const handlePlay = () => {
59
+ setIsModalOpen(true);
60
+ };
61
+
62
+ const handleModalClose = () => {
63
+ setIsModalOpen(false);
64
  };
65
 
66
  if (loading) {
 
176
  </div>
177
  )}
178
  </div>
179
+ <PlayModal
180
+ isOpen={isModalOpen}
181
+ onRequestClose={handleModalClose}
182
+ videoUrl={videoUrl}
183
+ setVideoUrl={setVideoUrl}
184
+ downloadProgress={downloadProgress}
185
+ title={title}
186
+ />
187
  </div>
188
  );
189
  }
nginx.conf CHANGED
@@ -14,6 +14,18 @@ http {
14
  tcp_nopush on;
15
  tcp_nodelay on;
16
  keepalive_timeout 65;
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  # Improve performance and security
19
  gzip on;
 
14
  tcp_nopush on;
15
  tcp_nodelay on;
16
  keepalive_timeout 65;
17
+ types {
18
+ text/html html;
19
+ text/css css;
20
+ text/xml xml;
21
+ image/gif gif;
22
+ image/jpeg jpeg jpg;
23
+ application/javascript js;
24
+ application/json json;
25
+ application/xml xml;
26
+ application/rss+xml rss;
27
+ text/plain txt;
28
+ }
29
 
30
  # Improve performance and security
31
  gzip on;