Chrunos commited on
Commit
f647181
·
verified ·
1 Parent(s): 61f290f

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +27 -0
  2. app.py +222 -0
  3. requirements.txt +7 -0
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.11
5
+
6
+ # Create a non-root user
7
+ RUN useradd -m -u 1000 user
8
+ # Install FFmpeg as root
9
+ USER root
10
+ RUN apt-get update && apt-get install -y ffmpeg
11
+
12
+ # Switch to the non-root user
13
+ USER user
14
+ ENV PATH="/home/user/.local/bin:$PATH"
15
+
16
+ # Set the working directory
17
+ WORKDIR /app
18
+
19
+ # Install Python dependencies
20
+ COPY --chown=user ./requirements.txt requirements.txt
21
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
22
+
23
+ # Copy application files
24
+ COPY --chown=user . /app
25
+
26
+ # Command to run the application
27
+ CMD uvicorn app:app --host 0.0.0.0 --port $PORT
app.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from fastapi import FastAPI, Request, HTTPException
3
+ from fastapi.concurrency import run_in_threadpool
4
+ import yt_dlp
5
+ import urllib.parse
6
+ import os
7
+ from datetime import datetime, timedelta
8
+ from dotenv import load_dotenv
9
+ import tempfile
10
+ from pathlib import Path
11
+ from collections import defaultdict
12
+ import logging
13
+ import gc
14
+ from typing import Dict, Any
15
+ import cloudscraper
16
+
17
+
18
+ tmp_dir = tempfile.gettempdir()
19
+
20
+ logging.basicConfig(level=logging.INFO)
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+
25
+ load_dotenv()
26
+ app = FastAPI()
27
+
28
+
29
+ # Define a global temporary download directory
30
+ global_download_dir = tempfile.mkdtemp()
31
+
32
+ # Rate limiting dictionary
33
+ class RateLimiter:
34
+ def __init__(self, max_requests: int, time_window: timedelta):
35
+ self.max_requests = max_requests
36
+ self.time_window = time_window
37
+ self.requests: Dict[str, list] = defaultdict(list)
38
+
39
+ def _cleanup_old_requests(self, user_ip: str) -> None:
40
+ """Remove requests that are outside the time window."""
41
+ current_time = time.time()
42
+ self.requests[user_ip] = [
43
+ timestamp for timestamp in self.requests[user_ip]
44
+ if current_time - timestamp < self.time_window.total_seconds()
45
+ ]
46
+
47
+ def is_rate_limited(self, user_ip: str) -> bool:
48
+ """Check if the user has exceeded their rate limit."""
49
+ self._cleanup_old_requests(user_ip)
50
+
51
+ # Get current count after cleanup
52
+ current_count = len(self.requests[user_ip])
53
+
54
+ # Add current request timestamp (incrementing the count)
55
+ current_time = time.time()
56
+ self.requests[user_ip].append(current_time)
57
+
58
+ # Check if user has exceeded the maximum requests
59
+ return (current_count + 1) > self.max_requests
60
+
61
+ def get_current_count(self, user_ip: str) -> int:
62
+ """Get the current request count for an IP."""
63
+ self._cleanup_old_requests(user_ip)
64
+ return len(self.requests[user_ip])
65
+
66
+
67
+ # Initialize rate limiter with 100 requests per day
68
+ rate_limiter = RateLimiter(
69
+ max_requests=12,
70
+ time_window=timedelta(days=1)
71
+ )
72
+
73
+ def get_user_ip(request: Request) -> str:
74
+ """Helper function to get user's IP address."""
75
+ forwarded = request.headers.get("X-Forwarded-For")
76
+ if forwarded:
77
+ return forwarded.split(",")[0]
78
+ return request.client.host
79
+
80
+
81
+
82
+ restricted_domain = "chrunos.com"
83
+
84
+
85
+
86
+ TRACT_API = os.getenv("EXTRACT_API")
87
+ ALT_API = os.getenv("ALT_API")
88
+ target_domains = [
89
+ "pornhub.com",
90
+ "xhmamster.com",
91
+ "eporner.com",
92
+ "youporn.com"
93
+ ]
94
+
95
+ def extract_video_info(video_url: str) -> str:
96
+ if any(domain in video_url for domain in target_domains):
97
+ EXTRACT_API = TRACT_API
98
+ else:
99
+ EXTRACT_API = ALT_API
100
+ api_url = f'{EXTRACT_API}?url={video_url}'
101
+ logger.info(api_url)
102
+ session = cloudscraper.create_scraper()
103
+ try:
104
+ response = session.get(api_url, timeout=20)
105
+
106
+ if response.status_code == 200:
107
+ json_response = response.json()
108
+ result = []
109
+ # 检查 formats 列表是否存在且不为空
110
+ if 'formats' in json_response:
111
+ for format_item in json_response['formats']:
112
+ format_url = format_item.get('url')
113
+ format_id = format_item.get('format_id')
114
+ p_cookies = format_item.get('cookies')
115
+ if format_id and format_url:
116
+ result.append({
117
+ "url": format_url,
118
+ "format_id": format_id,
119
+ "cookies": p_cookies
120
+ })
121
+
122
+ title = json_response.get('title')
123
+ logger.info(title)
124
+ if "pornhub.com" in video_url:
125
+ p_result = [item for item in result if 'hls' in item['format_id']]
126
+ last_item = p_result[-1]
127
+ second_last_item = p_result[-2]
128
+ last_item["format_id"] = f'{last_item["format_id"]} - Chrunos Shortcuts Premium Only'
129
+ last_item["url"] = 'https://chrunos.com/premium-shortcuts/'
130
+ second_last_item["format_id"] = f'{second_last_item["format_id"]} - Chrunos Shortcuts Premium Only'
131
+ second_last_item["url"] = 'https://chrunos.com/premium-shortcuts/'
132
+ return p_result
133
+ else:
134
+ new_result = result
135
+ # Check if new_result has more than one item
136
+ if len(new_result) > 3:
137
+ for i in range(3, len(new_result)):
138
+ item = new_result[i]
139
+ item["format_id"] = f'{item["format_id"]} - Chrunos Shortcuts Premium Only'
140
+ item["url"] = 'https://chrunos.com/premium-shortcuts/'
141
+ elif 2 <= len(new_result) <= 3:
142
+ last_item = new_result[-1]
143
+ last_item["format_id"] = f'{last_item["format_id"]} - Chrunos Shortcuts Premium Only'
144
+ last_item["url"] = 'https://chrunos.com/premium-shortcuts/'
145
+ elif len(new_result) == 1:
146
+ new_item = {"url": "https://chrunos.com/premium-shortcuts/",
147
+ "format_id": "Best Qaulity Video - Chrunos Shortcuts Premium Only"
148
+ }
149
+ new_result.append(new_item)
150
+
151
+ return new_result
152
+ else:
153
+ if 'url' in json_response:
154
+ download_url = json_response.get('url')
155
+ thumbnail_url = json_response.get('thumbnail')
156
+ return [
157
+ {"url": download_url,
158
+ "format_id": "Normal Quality Video"
159
+ },
160
+ {"url": thumbnail_url,
161
+ "format_id": "thumbnail"},
162
+ {"url": "https://chrunos.com/premium-shortcuts/",
163
+ "format_id": "Best Qaulity Video - Chrunos Shortcuts Premium Only"}
164
+ ]
165
+ return {"error": "No formats available. Report Error on Telegram"}
166
+ else:
167
+ return {"error": f"Request failed with status code {response.status_code}, API: {api_url}"}
168
+ except Exception as e:
169
+ logger.error(f"An error occurred: {e}")
170
+ return {"error": str(e)}
171
+
172
+
173
+ @app.post("/test")
174
+ async def test_download(request: Request):
175
+ user_ip = get_user_ip(request)
176
+ if rate_limiter.is_rate_limited(user_ip):
177
+ current_count = rate_limiter.get_current_count(user_ip)
178
+ raise HTTPException(
179
+ status_code=429,
180
+ detail={
181
+ "error": "You have exceeded the maximum number of requests per day. Please try again tomorrow.",
182
+ "url": "https://t.me/chrunoss"
183
+ }
184
+ )
185
+ data = await request.json()
186
+ video_url = data.get('url')
187
+ response = extract_video_info(video_url)
188
+ return response
189
+
190
+
191
+ @app.post("/hls")
192
+ async def download_hls_video(request: Request):
193
+ data = await request.json()
194
+ hls_url = data.get('url')
195
+ base_url = str(request.base_url)
196
+
197
+ timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
198
+ output_template = str(Path(global_download_dir) / f'%(title)s_{timestamp}.%(ext)s')
199
+
200
+ ydl_opts = {
201
+ 'format': 'best',
202
+ 'outtmpl': output_template,
203
+ 'quiet': True,
204
+ 'no_warnings': True,
205
+ 'noprogress': True,
206
+ 'merge_output_format': 'mp4'
207
+ }
208
+
209
+ try:
210
+ await run_in_threadpool(lambda: yt_dlp.YoutubeDL(ydl_opts).download([hls_url]))
211
+ except Exception as e:
212
+ return {"error": f"Download failed: {str(e)}"}
213
+
214
+ downloaded_files = list(Path(global_download_dir).glob(f"*_{timestamp}.mp4"))
215
+ if not downloaded_files:
216
+ return {"error": "Download failed"}
217
+
218
+ downloaded_file = downloaded_files[0]
219
+ encoded_filename = urllib.parse.quote(downloaded_file.name)
220
+ download_url = f"{base_url}file/{encoded_filename}"
221
+ gc.collect()
222
+ return {"url": download_url}
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ yt-dlp
3
+ ffmpeg-python
4
+ uvicorn
5
+ python-dotenv
6
+ Pillow
7
+ cloudscraper