113 lines
4.0 KiB
Python
113 lines
4.0 KiB
Python
from instagrapi.exceptions import LoginRequired
|
|
from sqlalchemy.orm import Session
|
|
from database import InstagramAccount, InstagramPost
|
|
from utils.instagram_client import get_logged_in_client
|
|
from utils.s3_uploader import upload_media_from_url
|
|
from schemas import ScrapeRequest, ScrapedPost, MediaItem
|
|
from typing import List
|
|
from datetime import datetime
|
|
|
|
|
|
def scrape_and_store(db: Session, req: ScrapeRequest) -> List[ScrapedPost]:
|
|
client = get_logged_in_client()
|
|
|
|
try:
|
|
user_id = client.user_id_from_username(req.username)
|
|
posts = client.user_medias_v1(user_id, amount=30) # fetch more to skip duplicates
|
|
except Exception as e:
|
|
print(f"❌ Failed to fetch posts for @{req.username}: {e}")
|
|
return []
|
|
|
|
# Get or create InstagramAccount
|
|
account = db.query(InstagramAccount).filter_by(username=req.username).first()
|
|
if not account:
|
|
account = InstagramAccount(
|
|
username=req.username,
|
|
seller_id=req.seller_id,
|
|
is_active=True,
|
|
last_synced=None
|
|
)
|
|
db.add(account)
|
|
db.commit()
|
|
db.refresh(account)
|
|
|
|
result: List[ScrapedPost] = []
|
|
new_post_count = 0
|
|
|
|
for post in posts:
|
|
post_id = str(post.id)
|
|
|
|
# Skip if already in DB
|
|
if db.query(InstagramPost).filter_by(ig_post_id=post_id).first():
|
|
continue
|
|
|
|
media_items = []
|
|
|
|
try:
|
|
# Handle carousel
|
|
if post.media_type == 8 and hasattr(post, "resources"):
|
|
for res in post.resources:
|
|
media_url = res.video_url if res.media_type == 2 else res.thumbnail_url
|
|
if not media_url:
|
|
continue
|
|
local_url = upload_media_from_url(media_url, str(req.seller_id), post_id)
|
|
if local_url:
|
|
media_items.append(MediaItem(
|
|
media_type="video" if res.media_type == 2 else "image",
|
|
media_url=media_url,
|
|
local_media=local_url
|
|
))
|
|
else:
|
|
media_url = post.video_url if post.media_type == 2 else post.thumbnail_url
|
|
if not media_url:
|
|
continue
|
|
local_url = upload_media_from_url(media_url, str(req.seller_id), post_id)
|
|
if local_url:
|
|
media_items.append(MediaItem(
|
|
media_type="video" if post.media_type == 2 else "image",
|
|
media_url=media_url,
|
|
local_media=local_url
|
|
))
|
|
except LoginRequired:
|
|
print("⛔ Login expired or failed mid-request.")
|
|
break
|
|
except Exception as e:
|
|
print(f"⚠️ Error downloading media: {e}")
|
|
continue
|
|
|
|
if not media_items:
|
|
continue
|
|
|
|
# ✅ Upload thumbnail separately and store local version
|
|
uploaded_thumb = upload_media_from_url(post.thumbnail_url, str(req.seller_id), f"{post_id}/thumbnail")
|
|
final_thumbnail = uploaded_thumb or post.thumbnail_url # fallback if upload fails
|
|
|
|
# Save to DB
|
|
post_entry = InstagramPost(
|
|
ig_post_id=post_id,
|
|
account_id=account.id,
|
|
media_type="carousel" if post.media_type == 8 else ("video" if post.media_type == 2 else "image"),
|
|
caption=post.caption_text or "",
|
|
thumbnail_url=final_thumbnail,
|
|
local_media=[m.dict() for m in media_items],
|
|
created_at=post.taken_at or datetime.utcnow(),
|
|
)
|
|
db.add(post_entry)
|
|
db.commit()
|
|
|
|
# Add to response
|
|
result.append(ScrapedPost(
|
|
ig_post_id=post_id,
|
|
media_type=post_entry.media_type,
|
|
caption=post_entry.caption,
|
|
thumbnail_url=final_thumbnail,
|
|
local_media=media_items,
|
|
created_at=post_entry.created_at
|
|
))
|
|
|
|
new_post_count += 1
|
|
if new_post_count >= req.max_count:
|
|
break
|
|
|
|
return result
|