coollsd commited on
Commit
75193ce
·
verified ·
1 Parent(s): b61b351

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +107 -106
app.py CHANGED
@@ -1,133 +1,131 @@
1
  from fastapi import FastAPI, File, UploadFile, Request
2
- from fastapi.responses import HTMLResponse, RedirectResponse, StreamingResponse
3
  import requests
4
  from urllib.parse import urljoin
5
  import time
6
  from typing import Dict
 
7
 
8
  app = FastAPI()
9
 
10
- HTML_CONTENT = """
11
  <!DOCTYPE html>
12
  <html lang="en">
13
  <head>
14
  <meta charset="UTF-8">
15
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
16
- <title>Pro File Uploader</title>
17
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
 
18
  <style>
19
  body {
20
- font-family: 'Roboto', sans-serif;
21
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
22
  height: 100vh;
23
- margin: 0;
24
  display: flex;
25
  justify-content: center;
26
  align-items: center;
27
- }
28
- .container {
29
- background: rgba(255, 255, 255, 0.9);
30
- padding: 2rem;
31
- border-radius: 10px;
32
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
33
- text-align: center;
34
- max-width: 400px;
35
- width: 100%;
36
  }
37
  h1 {
38
- color: #4a4a4a;
39
- margin-bottom: 1.5rem;
40
- }
41
- .file-input {
42
- display: none;
43
  }
44
- .file-label {
45
- background-color: #3498db;
46
- color: white;
47
- padding: 10px 20px;
48
- border-radius: 5px;
49
- cursor: pointer;
50
- transition: background-color 0.3s ease;
51
- }
52
- .file-label:hover {
53
- background-color: #2980b9;
54
- }
55
- .file-name {
56
- margin-top: 1rem;
57
- font-size: 0.9rem;
58
- color: #666;
59
  }
60
- .progress-bar {
61
- width: 100%;
62
- height: 10px;
63
- background-color: #e0e0e0;
64
- border-radius: 5px;
65
- margin-top: 1rem;
66
- overflow: hidden;
67
- display: none;
68
  }
69
  .progress {
70
- width: 0%;
71
- height: 100%;
72
- background-color: #2ecc71;
73
- transition: width 0.5s ease;
74
  }
75
- @keyframes pulse {
76
- 0% { transform: scale(1); }
77
- 50% { transform: scale(1.05); }
78
- 100% { transform: scale(1); }
79
  }
80
- .pulse {
81
- animation: pulse 2s infinite;
 
82
  }
83
  </style>
84
  </head>
85
  <body>
86
- <div class="container">
87
- <h1>Pro File Uploader</h1>
88
- <form id="uploadForm" action="/upload" method="post" enctype="multipart/form-data">
89
- <input type="file" name="file" id="file" class="file-input" accept="*/*" required>
90
- <label for="file" class="file-label">Choose File</label>
91
- <div class="file-name" id="fileName"></div>
92
- <div class="progress-bar" id="progressBar">
93
- <div class="progress" id="progress"></div>
94
- </div>
95
- </form>
96
- </div>
97
-
 
 
 
 
 
 
98
  <script>
99
- const fileInput = document.getElementById('file');
100
- const fileName = document.getElementById('fileName');
101
- const uploadForm = document.getElementById('uploadForm');
102
- const progressBar = document.getElementById('progressBar');
103
- const progress = document.getElementById('progress');
104
-
105
- fileInput.addEventListener('change', (e) => {
106
- if (e.target.files.length > 0) {
107
- fileName.textContent = e.target.files[0].name;
108
- uploadForm.submit();
109
- showProgress();
110
- }
111
- });
112
-
113
- function showProgress() {
114
- progressBar.style.display = 'block';
115
- let width = 0;
116
- const interval = setInterval(() => {
117
- if (width >= 100) {
118
- clearInterval(interval);
119
- } else {
120
- width++;
121
- progress.style.width = width + '%';
122
  }
123
- }, 20);
124
- }
125
-
126
- document.querySelector('.file-label').classList.add('pulse');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  </script>
128
  </body>
129
  </html>
130
- """
131
 
132
  @app.get("/", response_class=HTMLResponse)
133
  async def index():
@@ -136,24 +134,27 @@ async def index():
136
  @app.post("/upload")
137
  async def handle_upload(file: UploadFile = File(...)):
138
  if not file.filename:
139
- return {"error": "No file selected."}, 400
140
 
141
  cookies = await get_cookies()
142
  if 'csrftoken' not in cookies or 'sessionid' not in cookies:
143
- return {"error": "Failed to obtain necessary cookies"}, 500
144
 
145
- upload_result = await initiate_upload(cookies, file.filename, file.content_type)
 
 
 
146
  if not upload_result or 'upload_url' not in upload_result:
147
- return {"error": "Failed to initiate upload"}, 500
148
 
149
  file_content = await file.read()
150
  upload_success = await retry_upload(upload_result['upload_url'], file_content, file.content_type)
151
  if not upload_success:
152
- return {"error": "File upload failed after multiple attempts"}, 500
153
 
154
  original_url = upload_result['serving_url']
155
  mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
156
- return RedirectResponse(url=mirrored_url, status_code=302)
157
 
158
  @app.get("/rbxg/{path:path}")
159
  async def handle_video_stream(path: str, request: Request):
@@ -167,13 +168,13 @@ async def handle_video_stream(path: str, request: Request):
167
  for chunk in response.iter_content(chunk_size=8192):
168
  yield chunk
169
 
170
- headers = dict(response.headers)
171
- headers['Access-Control-Allow-Origin'] = '*'
172
 
173
  if response.status_code == 206:
174
- headers['Content-Range'] = response.headers.get('Content-Range')
175
 
176
- return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
177
 
178
  async def get_cookies() -> Dict[str, str]:
179
  try:
@@ -193,7 +194,7 @@ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type:
193
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
194
  'Referer': 'https://replicate.com/levelsio/neon-tokyo',
195
  'Origin': 'https://replicate.com',
196
- 'Accept': '*/*',
197
  'Accept-Language': 'en-US,en;q=0.5',
198
  'Accept-Encoding': 'identity',
199
  'Sec-Fetch-Dest': 'empty',
 
1
  from fastapi import FastAPI, File, UploadFile, Request
2
+ from fastapi.responses import HTMLResponse, StreamingResponse, JSONResponse
3
  import requests
4
  from urllib.parse import urljoin
5
  import time
6
  from typing import Dict
7
+ import uvicorn
8
 
9
  app = FastAPI()
10
 
11
+ HTML_CONTENT = '''
12
  <!DOCTYPE html>
13
  <html lang="en">
14
  <head>
15
  <meta charset="UTF-8">
16
+ <title>File Upload</title>
17
+ <!-- Include Bootstrap CSS for styling -->
18
+ <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
19
+ <!-- Include custom styles -->
20
  <style>
21
  body {
22
+ background: linear-gradient(135deg, #1abc9c, #3498db);
23
+ color: #fff;
24
  height: 100vh;
 
25
  display: flex;
26
  justify-content: center;
27
  align-items: center;
28
+ flex-direction: column;
29
+ font-family: Arial, sans-serif;
30
+ overflow: hidden;
 
 
 
 
 
 
31
  }
32
  h1 {
33
+ margin-bottom: 30px;
34
+ animation: fadeInDown 1s;
 
 
 
35
  }
36
+ form {
37
+ background: rgba(255, 255, 255, 0.1);
38
+ padding: 30px;
39
+ border-radius: 10px;
40
+ animation: fadeInUp 1s;
 
 
 
 
 
 
 
 
 
 
41
  }
42
+ .btn-upload {
43
+ margin-top: 20px;
 
 
 
 
 
 
44
  }
45
  .progress {
46
+ margin-top: 20px;
47
+ display: none;
 
 
48
  }
49
+ @keyframes fadeInDown {
50
+ from {opacity: 0; transform: translateY(-50px);}
51
+ to {opacity: 1; transform: translateY(0);}
 
52
  }
53
+ @keyframes fadeInUp {
54
+ from {opacity: 0; transform: translateY(50px);}
55
+ to {opacity: 1; transform: translateY(0);}
56
  }
57
  </style>
58
  </head>
59
  <body>
60
+ <h1>Upload Your File</h1>
61
+ <form id="upload-form">
62
+ <div class="custom-file">
63
+ <input type="file" class="custom-file-input" id="file-input" name="file" accept="/" required>
64
+ <label class="custom-file-label" for="file-input">Choose file...</label>
65
+ </div>
66
+ <button type="submit" class="btn btn-primary btn-upload">Upload</button>
67
+ <div class="progress">
68
+ <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;"></div>
69
+ </div>
70
+ </form>
71
+
72
+ <!-- Include jQuery and Bootstrap JS for handling events and styles -->
73
+ <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
74
+ <!-- Include Popper and Bootstrap JS -->
75
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
76
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
77
+ <!-- Custom script -->
78
  <script>
79
+ $(document).ready(function(){
80
+ $('#file-input').on('change', function(){
81
+ // Get the file name and display it
82
+ var fileName = $(this).val().split('\\').pop();
83
+ $(this).next('.custom-file-label').html(fileName);
84
+ });
85
+
86
+ $('#upload-form').on('submit', function(e){
87
+ e.preventDefault();
88
+ var formData = new FormData();
89
+ var fileInput = $('#file-input')[0];
90
+ if (fileInput.files.length === 0) {
91
+ alert('Please select a file to upload.');
92
+ return;
 
 
 
 
 
 
 
 
 
93
  }
94
+ formData.append('file', fileInput.files[0]);
95
+ $('.progress').show();
96
+ $.ajax({
97
+ url: '/upload',
98
+ type: 'POST',
99
+ data: formData,
100
+ contentType: false,
101
+ processData: false,
102
+ xhr: function(){
103
+ var xhr = new window.XMLHttpRequest();
104
+ xhr.upload.addEventListener('progress', function(e){
105
+ if (e.lengthComputable) {
106
+ var percentComplete = (e.loaded / e.total) * 100;
107
+ $('.progress-bar').css('width', percentComplete + '%');
108
+ $('.progress-bar').attr('aria-valuenow', percentComplete);
109
+ }
110
+ }, false);
111
+ return xhr;
112
+ },
113
+ success: function(response){
114
+ window.location.href = response.url;
115
+ },
116
+ error: function(jqXHR){
117
+ var errorMsg = jqXHR.responseJSON && jqXHR.responseJSON.error ? jqXHR.responseJSON.error : 'File upload failed.';
118
+ alert(errorMsg);
119
+ $('.progress').hide();
120
+ $('.progress-bar').css('width', '0%');
121
+ }
122
+ });
123
+ });
124
+ });
125
  </script>
126
  </body>
127
  </html>
128
+ '''
129
 
130
  @app.get("/", response_class=HTMLResponse)
131
  async def index():
 
134
  @app.post("/upload")
135
  async def handle_upload(file: UploadFile = File(...)):
136
  if not file.filename:
137
+ return JSONResponse(content={"error": "No file selected."}, status_code=400)
138
 
139
  cookies = await get_cookies()
140
  if 'csrftoken' not in cookies or 'sessionid' not in cookies:
141
+ return JSONResponse(content={"error": "Failed to obtain necessary cookies."}, status_code=500)
142
 
143
+ try:
144
+ upload_result = await initiate_upload(cookies, file.filename, file.content_type)
145
+ except Exception as e:
146
+ return JSONResponse(content={"error": f"Failed to initiate upload: {e}"}, status_code=500)
147
  if not upload_result or 'upload_url' not in upload_result:
148
+ return JSONResponse(content={"error": "Failed to initiate upload."}, status_code=500)
149
 
150
  file_content = await file.read()
151
  upload_success = await retry_upload(upload_result['upload_url'], file_content, file.content_type)
152
  if not upload_success:
153
+ return JSONResponse(content={"error": "File upload failed after multiple attempts."}, status_code=500)
154
 
155
  original_url = upload_result['serving_url']
156
  mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
157
+ return JSONResponse(content={"url": mirrored_url})
158
 
159
  @app.get("/rbxg/{path:path}")
160
  async def handle_video_stream(path: str, request: Request):
 
168
  for chunk in response.iter_content(chunk_size=8192):
169
  yield chunk
170
 
171
+ response_headers = dict(response.headers)
172
+ response_headers['Access-Control-Allow-Origin'] = ''
173
 
174
  if response.status_code == 206:
175
+ response_headers['Content-Range'] = response.headers.get('Content-Range')
176
 
177
+ return StreamingResponse(generate(), status_code=response.status_code, headers=response_headers)
178
 
179
  async def get_cookies() -> Dict[str, str]:
180
  try:
 
194
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
195
  'Referer': 'https://replicate.com/levelsio/neon-tokyo',
196
  'Origin': 'https://replicate.com',
197
+ 'Accept': '/*',
198
  'Accept-Language': 'en-US,en;q=0.5',
199
  'Accept-Encoding': 'identity',
200
  'Sec-Fetch-Dest': 'empty',