Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -142,16 +142,36 @@ RAW_USERNAMES = os.getenv("RAW_USERNAMES", "").split(",")
|
|
142 |
REACTED_USERNAMES = os.getenv("REACTED_USERNAMES", "").split(",")
|
143 |
|
144 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
def get_user_id(username: str) -> Optional[str]:
|
146 |
try:
|
147 |
res = requests.get(
|
148 |
f"https://www.instagram.com/api/v1/users/web_profile_info/?username={username}",
|
|
|
149 |
timeout=10
|
150 |
)
|
151 |
if res.status_code == 200:
|
152 |
-
|
153 |
-
|
154 |
-
|
|
|
|
|
|
|
155 |
return None
|
156 |
|
157 |
def get_links(user_id: str, limit: int = 5) -> list[Tuple[str, str]]:
|
@@ -171,8 +191,9 @@ def get_links(user_id: str, limit: int = 5) -> list[Tuple[str, str]]:
|
|
171 |
}
|
172 |
|
173 |
try:
|
174 |
-
res = requests.get("https://www.instagram.com/graphql/query/", params=params, timeout=10)
|
175 |
if res.status_code != 200:
|
|
|
176 |
break
|
177 |
|
178 |
media = res.json()["data"]["user"]["edge_owner_to_timeline_media"]
|
@@ -187,47 +208,60 @@ def get_links(user_id: str, limit: int = 5) -> list[Tuple[str, str]]:
|
|
187 |
if not media["page_info"]["has_next_page"]:
|
188 |
break
|
189 |
end_cursor = media["page_info"]["end_cursor"]
|
190 |
-
except:
|
|
|
191 |
break
|
|
|
192 |
return links
|
193 |
|
194 |
def send_to_bot_and_get_video(link: str) -> Optional[str]:
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
client(
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
|
|
|
|
|
|
|
|
|
|
218 |
return None
|
219 |
|
220 |
def fetch_valid_reel() -> Tuple[Optional[str], Optional[str]]:
|
221 |
with client:
|
222 |
pools = [(RAW_USERNAMES, "raw"), (REACTED_USERNAMES, "reacted")]
|
223 |
pools = [(p, t) for p, t in pools if p]
|
|
|
|
|
|
|
|
|
224 |
random.shuffle(pools)
|
225 |
|
226 |
for usernames, reel_type in pools:
|
227 |
random.shuffle(usernames)
|
228 |
for username in usernames:
|
|
|
229 |
uid = get_user_id(username)
|
230 |
if not uid:
|
|
|
231 |
continue
|
232 |
|
233 |
fetch_idx = get_next_fetch_index(username)
|
@@ -235,10 +269,13 @@ def fetch_valid_reel() -> Tuple[Optional[str], Optional[str]]:
|
|
235 |
|
236 |
for idx, (reel_id, link) in enumerate(links[fetch_idx:], start=fetch_idx):
|
237 |
if is_reel_fetched_or_skipped(username, reel_id):
|
|
|
238 |
continue
|
239 |
|
|
|
240 |
video_path = send_to_bot_and_get_video(link)
|
241 |
if not video_path:
|
|
|
242 |
mark_reel_skipped(username, reel_id)
|
243 |
continue
|
244 |
|
@@ -248,6 +285,7 @@ def fetch_valid_reel() -> Tuple[Optional[str], Optional[str]]:
|
|
248 |
|
249 |
update_fetch_index(username, fetch_idx + len(links))
|
250 |
|
|
|
251 |
return None, None
|
252 |
|
253 |
def patch_moviepy():
|
|
|
142 |
REACTED_USERNAMES = os.getenv("REACTED_USERNAMES", "").split(",")
|
143 |
|
144 |
|
145 |
+
import os
|
146 |
+
import json
|
147 |
+
import time
|
148 |
+
import random
|
149 |
+
import pathlib
|
150 |
+
import requests
|
151 |
+
from typing import Optional, Tuple
|
152 |
+
from telethon.tl.functions.messages import GetHistoryRequest
|
153 |
+
|
154 |
+
# Constants
|
155 |
+
IG_HEADERS = {
|
156 |
+
"User-Agent": "Mozilla/5.0",
|
157 |
+
# Optional: Uncomment if using sessionid
|
158 |
+
# "Cookie": f"sessionid={os.getenv('IG_SESSION_ID')}"
|
159 |
+
}
|
160 |
+
|
161 |
def get_user_id(username: str) -> Optional[str]:
|
162 |
try:
|
163 |
res = requests.get(
|
164 |
f"https://www.instagram.com/api/v1/users/web_profile_info/?username={username}",
|
165 |
+
headers=IG_HEADERS,
|
166 |
timeout=10
|
167 |
)
|
168 |
if res.status_code == 200:
|
169 |
+
user_id = res.json()["data"]["user"]["id"]
|
170 |
+
logger.info(f"[π] Got user ID for @{username}: {user_id}")
|
171 |
+
return user_id
|
172 |
+
logger.warning(f"[β οΈ] Failed to get user ID for @{username}, status {res.status_code}")
|
173 |
+
except Exception as e:
|
174 |
+
logger.warning(f"[β] get_user_id({username}) error: {e}")
|
175 |
return None
|
176 |
|
177 |
def get_links(user_id: str, limit: int = 5) -> list[Tuple[str, str]]:
|
|
|
191 |
}
|
192 |
|
193 |
try:
|
194 |
+
res = requests.get("https://www.instagram.com/graphql/query/", params=params, headers=IG_HEADERS, timeout=10)
|
195 |
if res.status_code != 200:
|
196 |
+
logger.warning(f"[β οΈ] Failed to get links for user {user_id}, status {res.status_code}")
|
197 |
break
|
198 |
|
199 |
media = res.json()["data"]["user"]["edge_owner_to_timeline_media"]
|
|
|
208 |
if not media["page_info"]["has_next_page"]:
|
209 |
break
|
210 |
end_cursor = media["page_info"]["end_cursor"]
|
211 |
+
except Exception as e:
|
212 |
+
logger.warning(f"[β] get_links() error: {e}")
|
213 |
break
|
214 |
+
logger.info(f"[π] Found {len(links)} links for user ID {user_id}")
|
215 |
return links
|
216 |
|
217 |
def send_to_bot_and_get_video(link: str) -> Optional[str]:
|
218 |
+
try:
|
219 |
+
entity = client.loop.run_until_complete(client.get_entity("instasavegrambot"))
|
220 |
+
client.loop.run_until_complete(client.send_message(entity, link))
|
221 |
+
|
222 |
+
for _ in range(15): # Wait up to 30s
|
223 |
+
time.sleep(2)
|
224 |
+
history = client.loop.run_until_complete(
|
225 |
+
client(GetHistoryRequest(
|
226 |
+
peer=entity,
|
227 |
+
limit=2,
|
228 |
+
offset_id=0,
|
229 |
+
offset_date=None,
|
230 |
+
add_offset=0,
|
231 |
+
max_id=0,
|
232 |
+
min_id=0,
|
233 |
+
hash=0
|
234 |
+
))
|
235 |
+
)
|
236 |
+
for msg in history.messages:
|
237 |
+
logger.info(f"[π¨] Bot message: {msg.message or '[media]'}")
|
238 |
+
if msg.video and 20 <= msg.video.duration <= 180:
|
239 |
+
path = f"reels/{msg.id}.mp4"
|
240 |
+
pathlib.Path("reels").mkdir(exist_ok=True)
|
241 |
+
client.loop.run_until_complete(msg.download_media(file=path))
|
242 |
+
logger.info(f"[β
] Downloaded reel to {path}")
|
243 |
+
return path
|
244 |
+
except Exception as e:
|
245 |
+
logger.warning(f"[β] send_to_bot_and_get_video error: {e}")
|
246 |
return None
|
247 |
|
248 |
def fetch_valid_reel() -> Tuple[Optional[str], Optional[str]]:
|
249 |
with client:
|
250 |
pools = [(RAW_USERNAMES, "raw"), (REACTED_USERNAMES, "reacted")]
|
251 |
pools = [(p, t) for p, t in pools if p]
|
252 |
+
if not pools:
|
253 |
+
logger.warning("[β οΈ] No usernames in either RAW or REACTED pools.")
|
254 |
+
return None, None
|
255 |
+
|
256 |
random.shuffle(pools)
|
257 |
|
258 |
for usernames, reel_type in pools:
|
259 |
random.shuffle(usernames)
|
260 |
for username in usernames:
|
261 |
+
logger.info(f"[π] Trying @{username} ({reel_type})")
|
262 |
uid = get_user_id(username)
|
263 |
if not uid:
|
264 |
+
logger.warning(f"[π«] Skipping @{username}, failed to get user ID.")
|
265 |
continue
|
266 |
|
267 |
fetch_idx = get_next_fetch_index(username)
|
|
|
269 |
|
270 |
for idx, (reel_id, link) in enumerate(links[fetch_idx:], start=fetch_idx):
|
271 |
if is_reel_fetched_or_skipped(username, reel_id):
|
272 |
+
logger.info(f"[β©] Already processed: @{username}/{reel_id}")
|
273 |
continue
|
274 |
|
275 |
+
logger.info(f"[π₯] Sending link to bot: {link}")
|
276 |
video_path = send_to_bot_and_get_video(link)
|
277 |
if not video_path:
|
278 |
+
logger.warning(f"[β οΈ] Failed to fetch: {link}")
|
279 |
mark_reel_skipped(username, reel_id)
|
280 |
continue
|
281 |
|
|
|
285 |
|
286 |
update_fetch_index(username, fetch_idx + len(links))
|
287 |
|
288 |
+
logger.warning("[π«] No valid reels found after checking all usernames.")
|
289 |
return None, None
|
290 |
|
291 |
def patch_moviepy():
|