Chrunos commited on
Commit
d27a73d
·
verified ·
1 Parent(s): 226ea29

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -309
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from fastapi import FastAPI, HTTPException, status, Request, Form
2
  from fastapi.responses import StreamingResponse, HTMLResponse, RedirectResponse
3
  import requests
4
  import os
@@ -7,8 +7,6 @@ import logging
7
  import random
8
  import json
9
  from datetime import datetime, timedelta
10
- import re
11
- from typing import Optional
12
 
13
  # Configure logging
14
  logging.basicConfig(
@@ -39,174 +37,7 @@ USER_AGENTS = [
39
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
40
  ]
41
 
42
- # Cookie import HTML template
43
- COOKIE_IMPORT_HTML = """
44
- <!DOCTYPE html>
45
- <html>
46
- <head>
47
- <title>Import Instagram Session</title>
48
- <style>
49
- body { font-family: Arial; max-width: 700px; margin: 0 auto; padding: 20px; }
50
- .container { background-color: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 25px; }
51
- h1 { color: #262626; text-align: center; }
52
- .form-group { margin-bottom: 15px; }
53
- label { display: block; margin-bottom: 10px; }
54
- textarea { width: 100%; padding: 10px; border: 1px solid #dbdbdb; border-radius: 3px; height: 150px; }
55
- .btn { background-color: #0095f6; border: none; color: white; padding: 10px 15px; border-radius: 4px; cursor: pointer; font-weight: bold; }
56
- .btn-block { width: 100%%; }
57
- .status { margin-top: 20px; padding: 10px; border-radius: 4px; }
58
- .success { background-color: #e8f5e9; color: #388e3c; }
59
- .error { background-color: #ffebee; color: #d32f2f; }
60
- .instructions { background-color: #f8f9fa; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
61
- .code { font-family: monospace; background-color: #f1f1f1; padding: 2px 5px; border-radius: 3px; }
62
- ol { padding-left: 20px; }
63
- li { margin-bottom: 10px; }
64
- .tabs { display: flex; margin-bottom: 20px; }
65
- .tab { padding: 10px 15px; cursor: pointer; background-color: #f8f9fa; border-radius: 4px 4px 0 0; margin-right: 5px; }
66
- .tab.active { background-color: #0095f6; color: white; }
67
- .tab-content { display: none; }
68
- .tab-content.active { display: block; }
69
- </style>
70
- </head>
71
- <body>
72
- <div class="container">
73
- <h1>Import Instagram Session</h1>
74
-
75
- <div class="tabs">
76
- <div class="tab active" onclick="switchTab('tab1', this)">Cookie Format</div>
77
- <div class="tab" onclick="switchTab('tab2', this)">JSON Format</div>
78
- </div>
79
-
80
- <div id="tab1" class="tab-content active">
81
- <div class="instructions">
82
- <h3>How to get your Instagram cookies:</h3>
83
- <ol>
84
- <li>Log in to Instagram in your browser</li>
85
- <li>Open browser DevTools (F12 or right-click and select "Inspect")</li>
86
- <li>Go to the "Application" or "Storage" tab</li>
87
- <li>Under "Cookies", find "instagram.com"</li>
88
- <li>Find the <span class="code">sessionid</span> cookie and copy its value</li>
89
- <li>Also copy the <span class="code">ds_user_id</span> and <span class="code">csrftoken</span> cookies if available</li>
90
- <li>Paste the cookies in the format: <span class="code">name=value; name2=value2;</span></li>
91
- </ol>
92
- </div>
93
-
94
- <form method="post" action="/import-cookies">
95
- <div class="form-group">
96
- <label for="cookies">Paste your Instagram cookies here:</label>
97
- <textarea id="cookies" name="cookies" placeholder="sessionid=YOUR_SESSION_ID; ds_user_id=YOUR_USER_ID; csrftoken=YOUR_CSRF_TOKEN;" required></textarea>
98
- </div>
99
- <div class="form-group">
100
- <label for="username">Your Instagram username:</label>
101
- <input type="text" id="username" name="username" style="width: 100%; padding: 10px; border: 1px solid #dbdbdb; border-radius: 3px;" required>
102
- </div>
103
- <button type="submit" class="btn btn-block">Import Session</button>
104
- </form>
105
- </div>
106
-
107
- <div id="tab2" class="tab-content">
108
- <div class="instructions">
109
- <h3>How to get your cookies as JSON:</h3>
110
- <ol>
111
- <li>Install a browser extension like "EditThisCookie" or "Cookie-Editor"</li>
112
- <li>Log in to Instagram in your browser</li>
113
- <li>Click on the extension icon</li>
114
- <li>Choose "Export" or similar option to copy all cookies</li>
115
- <li>Paste the JSON data below</li>
116
- </ol>
117
- </div>
118
-
119
- <form method="post" action="/import-cookies-json">
120
- <div class="form-group">
121
- <label for="cookies_json">Paste your Instagram cookies JSON here:</label>
122
- <textarea id="cookies_json" name="cookies_json" placeholder='[{"name":"sessionid","value":"YOUR_SESSION_ID","domain":".instagram.com"}]' required></textarea>
123
- </div>
124
- <div class="form-group">
125
- <label for="username_json">Your Instagram username:</label>
126
- <input type="text" id="username_json" name="username" style="width: 100%; padding: 10px; border: 1px solid #dbdbdb; border-radius: 3px;" required>
127
- </div>
128
- <button type="submit" class="btn btn-block">Import Session</button>
129
- </form>
130
- </div>
131
-
132
- %s
133
- </div>
134
-
135
- <script>
136
- function switchTab(tabId, tabElement) {
137
- // Hide all tab contents
138
- document.querySelectorAll('.tab-content').forEach(content => {
139
- content.classList.remove('active');
140
- });
141
-
142
- // Deactivate all tabs
143
- document.querySelectorAll('.tab').forEach(tab => {
144
- tab.classList.remove('active');
145
- });
146
-
147
- // Activate clicked tab and its content
148
- document.getElementById(tabId).classList.add('active');
149
- tabElement.classList.add('active');
150
- }
151
- </script>
152
- </body>
153
- </html>
154
- """
155
-
156
- # Status page HTML
157
- STATUS_HTML = """
158
- <!DOCTYPE html>
159
- <html>
160
- <head>
161
- <title>API Status</title>
162
- <style>
163
- body { font-family: Arial; max-width: 600px; margin: 0 auto; padding: 20px; }
164
- .container { background-color: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 25px; }
165
- h1 { color: #262626; text-align: center; }
166
- .status { display: inline-block; padding: 5px 10px; border-radius: 4px; margin-left: 10px; font-weight: bold; }
167
- .connected { background-color: #e8f5e9; color: #388e3c; }
168
- .disconnected { background-color: #ffebee; color: #d32f2f; }
169
- .info { background-color: #f5f5f5; border-radius: 4px; padding: 15px; margin: 15px 0; }
170
- .btn { background-color: #0095f6; border: none; color: white; padding: 10px 15px; border-radius: 4px; text-decoration: none; display: inline-block; margin-right: 10px; }
171
- .btn-danger { background-color: #f44336; }
172
- table { width: 100%%; border-collapse: collapse; margin-top: 15px; }
173
- th, td { text-align: left; padding: 8px; border-bottom: 1px solid #ddd; }
174
- </style>
175
- </head>
176
- <body>
177
- <div class="container">
178
- <h1>Instagram API Status</h1>
179
-
180
- <h2>Connection Status
181
- %s
182
- </h2>
183
-
184
- <div class="info">
185
- %s
186
- </div>
187
-
188
- <a href="/import" class="btn">%s</a>
189
- %s
190
-
191
- <h2>API Endpoints</h2>
192
- <table>
193
- <tr><th>Endpoint</th><th>Description</th></tr>
194
- <tr><td><code>/stories/{username}</code></td><td>Get stories for a user</td></tr>
195
- <tr><td><code>/download/{url}</code></td><td>Download media from a URL</td></tr>
196
- <tr><td><code>/status</code></td><td>View API status</td></tr>
197
- </table>
198
-
199
- <h2>Cache Info</h2>
200
- <div class="info">
201
- <p>Cached users: %d</p>
202
- <p>Total stories cached: %d</p>
203
- </div>
204
- </div>
205
- </body>
206
- </html>
207
- """
208
-
209
- # Functions for cache and Instagram session
210
  def get_cache_key(username):
211
  return username.lower()
212
 
@@ -250,6 +81,7 @@ def should_rate_limit_request(username):
250
  LAST_REQUEST[key] = now
251
  return False
252
 
 
253
  def get_instagram_session():
254
  global IG_SESSION, IG_SESSION_EXPIRY
255
 
@@ -347,26 +179,7 @@ def parse_cookie_string(cookie_str):
347
 
348
  return cookies
349
 
350
- def validate_imported_session(session):
351
- """Check if the imported session seems valid"""
352
- # Check for essential cookies
353
- has_sessionid = any(c['name'] == 'sessionid' for c in session.cookies._cookies.get('.instagram.com', {}).get('/', {}).values())
354
-
355
- if not has_sessionid:
356
- return False
357
-
358
- # Try to make a basic request
359
- try:
360
- response = session.get('https://www.instagram.com/accounts/edit/', allow_redirects=False)
361
-
362
- # If we get redirected to login page, session is invalid
363
- if response.status_code == 302 and 'login' in response.headers.get('Location', ''):
364
- return False
365
-
366
- return response.status_code == 200
367
- except:
368
- return False
369
-
370
  def get_instagram_stories(username):
371
  session = get_instagram_session()
372
  if not session:
@@ -380,7 +193,6 @@ def get_instagram_stories(username):
380
  session.headers.update({
381
  'User-Agent': random.choice(USER_AGENTS),
382
  'X-IG-App-ID': '936619743392459',
383
- 'X-IG-WWW-Claim': 'hmac.AR1k18eNmvbsN7COEa9jpSQ9-NKGlK_TVqhgqxaGgxdCsIfR',
384
  'Referer': f'https://www.instagram.com/{username}/',
385
  })
386
 
@@ -458,19 +270,75 @@ def get_instagram_stories(username):
458
  logger.error(f"Error getting stories: {str(e)}")
459
  return {"error": "exception", "message": str(e)}
460
 
461
- # Web routes
462
  @app.get("/", response_class=HTMLResponse)
463
  async def root():
464
  return RedirectResponse(url="/status")
465
 
466
  @app.get("/import", response_class=HTMLResponse)
467
  async def import_page(message: str = None, success: bool = False):
468
- status_html = ""
469
- if message:
470
- status_class = "success" if success else "error"
471
- status_html = f'<div class="status {status_class}">{message}</div>'
472
 
473
- return HTMLResponse(COOKIE_IMPORT_HTML % status_html)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
474
 
475
  @app.post("/import-cookies")
476
  async def import_cookies(cookies: str = Form(...), username: str = Form(...)):
@@ -481,134 +349,109 @@ async def import_cookies(cookies: str = Form(...), username: str = Form(...)):
481
 
482
  if not cookie_objects:
483
  logger.warning("No valid cookies found in input")
484
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">No valid cookies found in input</div>')
485
 
486
  # Check for essential cookies
487
  has_sessionid = any(c['name'] == 'sessionid' for c in cookie_objects)
488
 
489
  if not has_sessionid:
490
  logger.warning("Missing essential sessionid cookie")
491
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Missing essential sessionid cookie</div>')
492
 
493
  # Save session
494
  if save_instagram_session(cookie_objects, username):
495
  # Verify session
496
  session = get_instagram_session()
497
  if session:
498
- # Try to make a basic request to test session
499
- try:
500
- response = session.get('https://www.instagram.com/accounts/edit/', allow_redirects=False)
501
-
502
- if response.status_code == 302 and 'login' in response.headers.get('Location', ''):
503
- logger.warning("Session seems invalid - redirected to login")
504
- clear_instagram_session()
505
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Session validation failed - redirected to login</div>')
506
-
507
- logger.info("Session validated successfully")
508
- return RedirectResponse(url="/status", status_code=303)
509
- except Exception as e:
510
- logger.error(f"Session validation error: {str(e)}")
511
- return HTMLResponse(COOKIE_IMPORT_HTML % f'<div class="status error">Session validation error: {str(e)}</div>')
512
  else:
513
  logger.error("Failed to get session after saving")
514
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Failed to create session</div>')
515
  else:
516
  logger.error("Failed to save session")
517
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Failed to save session</div>')
518
 
519
  except Exception as e:
520
  logger.error(f"Import error: {str(e)}")
521
- return HTMLResponse(COOKIE_IMPORT_HTML % f'<div class="status error">Import error: {str(e)}</div>')
522
-
523
- @app.post("/import-cookies-json")
524
- async def import_cookies_json(cookies_json: str = Form(...), username: str = Form(...)):
525
- logger.info(f"Importing JSON cookies for {username}")
526
-
527
- try:
528
- cookie_objects = json.loads(cookies_json)
529
-
530
- if not isinstance(cookie_objects, list):
531
- logger.warning("Invalid JSON format - expected array")
532
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Invalid JSON format - expected array of cookie objects</div>')
533
-
534
- # Check for essential cookies
535
- has_sessionid = any(c.get('name') == 'sessionid' for c in cookie_objects)
536
-
537
- if not has_sessionid:
538
- logger.warning("Missing essential sessionid cookie")
539
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Missing essential sessionid cookie</div>')
540
-
541
- # Normalize cookie objects
542
- for cookie in cookie_objects:
543
- if 'domain' not in cookie:
544
- cookie['domain'] = '.instagram.com'
545
-
546
- # Save session
547
- if save_instagram_session(cookie_objects, username):
548
- # Verify session
549
- session = get_instagram_session()
550
- if session:
551
- # Try to make a basic request to test session
552
- try:
553
- response = session.get('https://www.instagram.com/accounts/edit/', allow_redirects=False)
554
-
555
- if response.status_code == 302 and 'login' in response.headers.get('Location', ''):
556
- logger.warning("Session seems invalid - redirected to login")
557
- clear_instagram_session()
558
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Session validation failed - redirected to login</div>')
559
-
560
- logger.info("Session validated successfully")
561
- return RedirectResponse(url="/status", status_code=303)
562
- except Exception as e:
563
- logger.error(f"Session validation error: {str(e)}")
564
- return HTMLResponse(COOKIE_IMPORT_HTML % f'<div class="status error">Session validation error: {str(e)}</div>')
565
- else:
566
- logger.error("Failed to get session after saving")
567
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Failed to create session</div>')
568
- else:
569
- logger.error("Failed to save session")
570
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Failed to save session</div>')
571
-
572
- except json.JSONDecodeError:
573
- logger.error("Invalid JSON format")
574
- return HTMLResponse(COOKIE_IMPORT_HTML % '<div class="status error">Invalid JSON format</div>')
575
- except Exception as e:
576
- logger.error(f"Import error: {str(e)}")
577
- return HTMLResponse(COOKIE_IMPORT_HTML % f'<div class="status error">Import error: {str(e)}</div>')
578
-
579
- @app.get("/logout")
580
- async def logout():
581
- clear_instagram_session()
582
- return RedirectResponse(url="/status", status_code=303)
583
 
584
  @app.get("/status", response_class=HTMLResponse)
585
  async def status_page():
586
  session = get_instagram_session()
587
  session_valid = session is not None
588
 
589
- # Status badge
590
- status_badge = '<span class="status connected">Connected</span>' if session_valid else '<span class="status disconnected">Disconnected</span>'
 
 
 
591
 
592
- # Status info
593
  if session_valid:
594
- status_info = f"<p>✅ You are currently logged in as <strong>{IG_SESSION_USERNAME}</strong></p>"
595
- status_info += f"<p>Session will expire: {IG_SESSION_EXPIRY.strftime('%Y-%m-%d %H:%M:%S')}</p>"
596
  else:
597
- status_info = "<p>❌ No active Instagram session</p><p>Please import your cookies to use the API</p>"
598
 
599
- # Button text
600
- import_text = "Re-Import Cookies" if session_valid else "Import Cookies"
601
-
602
- # Logout button
603
- logout_button = '<a href="/logout" class="btn btn-danger">Logout</a>' if session_valid else ''
604
-
605
- # Cache info
606
- cache_count = len(STORY_CACHE)
607
- story_count = sum(len(data.get("data", [])) for data in STORY_CACHE.values())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
 
609
- return HTMLResponse(STATUS_HTML % (status_badge, status_info, import_text, logout_button, cache_count, story_count))
 
 
 
 
 
610
 
611
- # API routes
612
  @app.get("/stories/{username}")
613
  async def get_stories(username: str, cached: bool = False):
614
  logger.info(f"Request for @{username} stories")
@@ -727,6 +570,19 @@ async def download_media(url: str):
727
  detail="Failed to download media"
728
  )
729
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
  # Load cache and session at startup
731
  @app.on_event("startup")
732
  def startup_event():
@@ -754,17 +610,4 @@ def startup_event():
754
  logger.error(f"Cache loading error: {e}")
755
 
756
  # Try to load session
757
- get_instagram_session()
758
-
759
- # Health check endpoint
760
- @app.get("/health")
761
- async def health_check():
762
- session = get_instagram_session()
763
- return {
764
- "status": "ok",
765
- "authenticated": session is not None,
766
- "username": IG_SESSION_USERNAME,
767
- "session_expires": IG_SESSION_EXPIRY.isoformat() if IG_SESSION_EXPIRY else None,
768
- "cache_items": len(STORY_CACHE),
769
- "timestamp": datetime.now().isoformat()
770
- }
 
1
+ from fastapi import FastAPI, HTTPException, status, Form
2
  from fastapi.responses import StreamingResponse, HTMLResponse, RedirectResponse
3
  import requests
4
  import os
 
7
  import random
8
  import json
9
  from datetime import datetime, timedelta
 
 
10
 
11
  # Configure logging
12
  logging.basicConfig(
 
37
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
38
  ]
39
 
40
+ # Functions for cache management
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  def get_cache_key(username):
42
  return username.lower()
43
 
 
81
  LAST_REQUEST[key] = now
82
  return False
83
 
84
+ # Session management functions
85
  def get_instagram_session():
86
  global IG_SESSION, IG_SESSION_EXPIRY
87
 
 
179
 
180
  return cookies
181
 
182
+ # Instagram API functions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  def get_instagram_stories(username):
184
  session = get_instagram_session()
185
  if not session:
 
193
  session.headers.update({
194
  'User-Agent': random.choice(USER_AGENTS),
195
  'X-IG-App-ID': '936619743392459',
 
196
  'Referer': f'https://www.instagram.com/{username}/',
197
  })
198
 
 
270
  logger.error(f"Error getting stories: {str(e)}")
271
  return {"error": "exception", "message": str(e)}
272
 
273
+ # Routes
274
  @app.get("/", response_class=HTMLResponse)
275
  async def root():
276
  return RedirectResponse(url="/status")
277
 
278
  @app.get("/import", response_class=HTMLResponse)
279
  async def import_page(message: str = None, success: bool = False):
280
+ status_class = "success" if success else "error"
281
+ status_div = f'<div class="status {status_class}">{message}</div>' if message else ""
 
 
282
 
283
+ html = """
284
+ <!DOCTYPE html>
285
+ <html>
286
+ <head>
287
+ <title>Import Instagram Session</title>
288
+ <style>
289
+ body { font-family: Arial; max-width: 700px; margin: 0 auto; padding: 20px; }
290
+ .container { background-color: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 25px; }
291
+ h1 { color: #262626; text-align: center; }
292
+ .form-group { margin-bottom: 15px; }
293
+ label { display: block; margin-bottom: 10px; }
294
+ textarea { width: 100%; padding: 10px; border: 1px solid #dbdbdb; border-radius: 3px; height: 150px; }
295
+ input[type="text"] { width: 100%; padding: 10px; border: 1px solid #dbdbdb; border-radius: 3px; }
296
+ .btn { background-color: #0095f6; border: none; color: white; padding: 10px 15px; border-radius: 4px; cursor: pointer; font-weight: bold; width: 100%; }
297
+ .status { margin-top: 20px; padding: 10px; border-radius: 4px; }
298
+ .success { background-color: #e8f5e9; color: #388e3c; }
299
+ .error { background-color: #ffebee; color: #d32f2f; }
300
+ .instructions { background-color: #f8f9fa; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
301
+ .code { font-family: monospace; background-color: #f1f1f1; padding: 2px 5px; border-radius: 3px; }
302
+ ol { padding-left: 20px; }
303
+ li { margin-bottom: 10px; }
304
+ </style>
305
+ </head>
306
+ <body>
307
+ <div class="container">
308
+ <h1>Import Instagram Session</h1>
309
+
310
+ <div class="instructions">
311
+ <h3>How to get your Instagram cookies:</h3>
312
+ <ol>
313
+ <li>Log in to Instagram in your browser</li>
314
+ <li>Open browser DevTools (F12 or right-click and select "Inspect")</li>
315
+ <li>Go to the "Application" or "Storage" tab</li>
316
+ <li>Under "Cookies", find "instagram.com"</li>
317
+ <li>Find the <span class="code">sessionid</span> cookie and copy its value</li>
318
+ <li>Also copy the <span class="code">ds_user_id</span> and <span class="code">csrftoken</span> cookies if available</li>
319
+ <li>Paste the cookies in the format: <span class="code">name=value; name2=value2;</span></li>
320
+ </ol>
321
+ </div>
322
+
323
+ """ + status_div + """
324
+
325
+ <form method="post" action="/import-cookies">
326
+ <div class="form-group">
327
+ <label for="cookies">Paste your Instagram cookies here:</label>
328
+ <textarea id="cookies" name="cookies" placeholder="sessionid=YOUR_SESSION_ID; ds_user_id=YOUR_USER_ID; csrftoken=YOUR_CSRF_TOKEN;" required></textarea>
329
+ </div>
330
+ <div class="form-group">
331
+ <label for="username">Your Instagram username:</label>
332
+ <input type="text" id="username" name="username" required>
333
+ </div>
334
+ <button type="submit" class="btn">Import Session</button>
335
+ </form>
336
+ </div>
337
+ </body>
338
+ </html>
339
+ """
340
+
341
+ return HTMLResponse(content=html)
342
 
343
  @app.post("/import-cookies")
344
  async def import_cookies(cookies: str = Form(...), username: str = Form(...)):
 
349
 
350
  if not cookie_objects:
351
  logger.warning("No valid cookies found in input")
352
+ return import_page(message="No valid cookies found in input")
353
 
354
  # Check for essential cookies
355
  has_sessionid = any(c['name'] == 'sessionid' for c in cookie_objects)
356
 
357
  if not has_sessionid:
358
  logger.warning("Missing essential sessionid cookie")
359
+ return import_page(message="Missing essential sessionid cookie")
360
 
361
  # Save session
362
  if save_instagram_session(cookie_objects, username):
363
  # Verify session
364
  session = get_instagram_session()
365
  if session:
366
+ logger.info("Session imported successfully")
367
+ return RedirectResponse(url="/status", status_code=303)
 
 
 
 
 
 
 
 
 
 
 
 
368
  else:
369
  logger.error("Failed to get session after saving")
370
+ return import_page(message="Failed to create session")
371
  else:
372
  logger.error("Failed to save session")
373
+ return import_page(message="Failed to save session")
374
 
375
  except Exception as e:
376
  logger.error(f"Import error: {str(e)}")
377
+ return import_page(message=f"Import error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
 
379
  @app.get("/status", response_class=HTMLResponse)
380
  async def status_page():
381
  session = get_instagram_session()
382
  session_valid = session is not None
383
 
384
+ cache_count = len(STORY_CACHE)
385
+ story_count = sum(len(data.get("data", [])) for data in STORY_CACHE.values())
386
+
387
+ status_badge = 'Connected' if session_valid else 'Disconnected'
388
+ status_class = 'connected' if session_valid else 'disconnected'
389
 
 
390
  if session_valid:
391
+ status_info = f" Logged in as <strong>{IG_SESSION_USERNAME}</strong><br>"
392
+ status_info += f"Session expires: {IG_SESSION_EXPIRY.strftime('%Y-%m-%d %H:%M:%S')}"
393
  else:
394
+ status_info = " No active Instagram session<br>Please import your cookies to use the API"
395
 
396
+ html = f"""
397
+ <!DOCTYPE html>
398
+ <html>
399
+ <head>
400
+ <title>API Status</title>
401
+ <style>
402
+ body {{ font-family: Arial; max-width: 600px; margin: 0 auto; padding: 20px; }}
403
+ .container {{ background-color: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 25px; }}
404
+ h1 {{ color: #262626; text-align: center; }}
405
+ .status-badge {{ display: inline-block; padding: 5px 10px; border-radius: 4px; font-weight: bold; margin-left: 10px; }}
406
+ .connected {{ background-color: #e8f5e9; color: #388e3c; }}
407
+ .disconnected {{ background-color: #ffebee; color: #d32f2f; }}
408
+ .info {{ background-color: #f5f5f5; border-radius: 4px; padding: 15px; margin: 15px 0; }}
409
+ .btn {{ background-color: #0095f6; border: none; color: white; padding: 10px 15px; border-radius: 4px; text-decoration: none; display: inline-block; margin-right: 10px; }}
410
+ .btn-danger {{ background-color: #f44336; }}
411
+ table {{ width: 100%; border-collapse: collapse; margin-top: 15px; }}
412
+ th, td {{ text-align: left; padding: 8px; border-bottom: 1px solid #ddd; }}
413
+ </style>
414
+ </head>
415
+ <body>
416
+ <div class="container">
417
+ <h1>Instagram API Status</h1>
418
+
419
+ <h2>Connection Status
420
+ <span class="status-badge {status_class}">{status_badge}</span>
421
+ </h2>
422
+
423
+ <div class="info">
424
+ {status_info}
425
+ </div>
426
+
427
+ <a href="/import" class="btn">{"Re-Import Cookies" if session_valid else "Import Cookies"}</a>
428
+ {"<a href='/logout' class='btn btn-danger'>Logout</a>" if session_valid else ""}
429
+
430
+ <h2>API Endpoints</h2>
431
+ <table>
432
+ <tr><th>Endpoint</th><th>Description</th></tr>
433
+ <tr><td><code>/stories/{{username}}</code></td><td>Get stories for a user</td></tr>
434
+ <tr><td><code>/download/{{url}}</code></td><td>Download media from a URL</td></tr>
435
+ <tr><td><code>/status</code></td><td>View API status</td></tr>
436
+ </table>
437
+
438
+ <h2>Cache Info</h2>
439
+ <div class="info">
440
+ <p>Cached users: {cache_count}</p>
441
+ <p>Total stories cached: {story_count}</p>
442
+ </div>
443
+ </div>
444
+ </body>
445
+ </html>
446
+ """
447
 
448
+ return HTMLResponse(content=html)
449
+
450
+ @app.get("/logout")
451
+ async def logout():
452
+ clear_instagram_session()
453
+ return RedirectResponse(url="/status", status_code=303)
454
 
 
455
  @app.get("/stories/{username}")
456
  async def get_stories(username: str, cached: bool = False):
457
  logger.info(f"Request for @{username} stories")
 
570
  detail="Failed to download media"
571
  )
572
 
573
+ # Health check endpoint
574
+ @app.get("/health")
575
+ async def health_check():
576
+ session = get_instagram_session()
577
+ return {
578
+ "status": "ok",
579
+ "authenticated": session is not None,
580
+ "username": IG_SESSION_USERNAME,
581
+ "session_expires": IG_SESSION_EXPIRY.isoformat() if IG_SESSION_EXPIRY else None,
582
+ "cache_items": len(STORY_CACHE),
583
+ "timestamp": datetime.now().isoformat()
584
+ }
585
+
586
  # Load cache and session at startup
587
  @app.on_event("startup")
588
  def startup_event():
 
610
  logger.error(f"Cache loading error: {e}")
611
 
612
  # Try to load session
613
+ get_instagram_session()