182 lines
7.1 KiB
Python
182 lines
7.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Check Fansly attachments for 4K variants and upgrade if available.
|
|
|
|
This script:
|
|
1. Finds all non-4K video attachments from Fansly Direct
|
|
2. Re-fetches media info from the Fansly API
|
|
3. Checks if a higher resolution variant is available
|
|
4. Updates the attachment URL and resets for re-download if upgrade found
|
|
"""
|
|
|
|
import asyncio
|
|
import sys
|
|
import os
|
|
|
|
# Add project to path
|
|
sys.path.insert(0, '/opt/media-downloader')
|
|
|
|
# Bootstrap PostgreSQL adapter before any database imports
|
|
from modules.db_bootstrap import bootstrap_database
|
|
bootstrap_database()
|
|
|
|
from modules.paid_content.fansly_direct_client import FanslyDirectClient
|
|
from modules.paid_content.db_adapter import PaidContentDB
|
|
|
|
|
|
async def check_and_upgrade_attachments():
|
|
"""Check all non-4K Fansly attachments for upgrades."""
|
|
|
|
db = PaidContentDB('/opt/media-downloader/database/media_downloader.db')
|
|
|
|
# Get Fansly auth token
|
|
service = db.get_service('fansly_direct')
|
|
if not service or not service.get('session_cookie'):
|
|
print("ERROR: No Fansly auth token configured")
|
|
return
|
|
|
|
auth_token = service['session_cookie']
|
|
client = FanslyDirectClient(auth_token)
|
|
|
|
# Find non-4K video attachments from Fansly Direct
|
|
# 4K is 3840x2160 or 2160x3840 (portrait)
|
|
query = """
|
|
SELECT a.id, a.name, a.width, a.height, a.status, p.post_id, p.id as db_post_id
|
|
FROM paid_content_attachments a
|
|
JOIN paid_content_posts p ON a.post_id = p.id
|
|
JOIN paid_content_creators c ON p.creator_id = c.id
|
|
WHERE c.service_id = 'fansly_direct'
|
|
AND a.file_type = 'video'
|
|
AND a.width IS NOT NULL
|
|
AND a.height IS NOT NULL
|
|
AND NOT (
|
|
(a.width >= 3840 AND a.height >= 2160) OR
|
|
(a.width >= 2160 AND a.height >= 3840)
|
|
)
|
|
AND p.post_id NOT LIKE 'manual_%'
|
|
AND p.post_id NOT LIKE 'import_%'
|
|
AND p.post_id NOT LIKE '20%-%'
|
|
ORDER BY a.id
|
|
"""
|
|
|
|
cursor = db.conn.execute(query)
|
|
attachments = cursor.fetchall()
|
|
|
|
print(f"Found {len(attachments)} non-4K video attachments to check")
|
|
print("-" * 80)
|
|
|
|
upgrades_found = 0
|
|
errors = 0
|
|
already_best = 0
|
|
|
|
for att in attachments:
|
|
att_id, name, width, height, status, post_id, db_post_id = att
|
|
current_res = f"{width}x{height}"
|
|
|
|
print(f"\nChecking: {name} (ID: {att_id}, current: {current_res})")
|
|
|
|
try:
|
|
# Extract media ID from filename (e.g., "12345.mp4" -> "12345")
|
|
media_id = name.replace('.mp4', '').replace('.mov', '')
|
|
|
|
# Fetch media info from Fansly API
|
|
# We need to get the account media for this post
|
|
async with client:
|
|
# Get the post to find media info
|
|
posts, _, media_dict, account_media_dict, bundle_dict = await client._fetch_timeline_page(
|
|
account_id=None, # We'll search by post ID
|
|
before=str(int(post_id) + 1), # Get this post
|
|
account={}
|
|
)
|
|
|
|
# Find media in the dictionaries
|
|
found_4k = False
|
|
best_width = width
|
|
best_height = height
|
|
best_url = None
|
|
|
|
# Check account_media_dict for this media
|
|
for am_id, am_data in account_media_dict.items():
|
|
media = am_data.get('media', {})
|
|
if str(media.get('id')) == media_id:
|
|
# Found the media, check variants
|
|
variants = media.get('variants', [])
|
|
print(f" Found media with {len(variants)} variants")
|
|
|
|
for v in variants:
|
|
v_w = v.get('width', 0) or 0
|
|
v_h = v.get('height', 0) or 0
|
|
v_locs = v.get('locations', [])
|
|
|
|
# Check if this is a higher resolution
|
|
if v_w * v_h > best_width * best_height:
|
|
for loc in v_locs:
|
|
loc_url = loc.get('location', '')
|
|
# Prefer streaming formats for 4K
|
|
if '.m3u8' in loc_url or '.mp4' in loc_url or '.mov' in loc_url:
|
|
best_width = v_w
|
|
best_height = v_h
|
|
|
|
# Construct signed URL if metadata present
|
|
metadata = loc.get('metadata', {})
|
|
if metadata:
|
|
params = []
|
|
for key in ['Key-Pair-Id', 'Signature', 'Policy']:
|
|
if key in metadata:
|
|
params.append(f"{key}={metadata[key]}")
|
|
if params:
|
|
best_url = loc_url + '?' + '&'.join(params)
|
|
else:
|
|
best_url = loc_url
|
|
else:
|
|
best_url = loc_url
|
|
|
|
if v_w >= 3840 or v_h >= 3840:
|
|
found_4k = True
|
|
break
|
|
break
|
|
|
|
if found_4k and best_url:
|
|
print(f" ✓ UPGRADE FOUND: {best_width}x{best_height}")
|
|
upgrades_found += 1
|
|
|
|
# Update the attachment
|
|
db.conn.execute("""
|
|
UPDATE paid_content_attachments
|
|
SET download_url = ?,
|
|
width = ?,
|
|
height = ?,
|
|
status = 'pending',
|
|
download_attempts = 0,
|
|
error_message = NULL,
|
|
local_path = NULL,
|
|
local_filename = NULL
|
|
WHERE id = ?
|
|
""", (best_url, best_width, best_height, att_id))
|
|
db.conn.commit()
|
|
print(f" → Updated and queued for re-download")
|
|
|
|
elif best_width > width or best_height > height:
|
|
print(f" ~ Better quality available: {best_width}x{best_height} (not 4K)")
|
|
else:
|
|
print(f" - Already at best available quality")
|
|
already_best += 1
|
|
|
|
except Exception as e:
|
|
print(f" ✗ Error: {e}")
|
|
errors += 1
|
|
|
|
# Rate limiting
|
|
await asyncio.sleep(0.5)
|
|
|
|
print("\n" + "=" * 80)
|
|
print(f"Summary:")
|
|
print(f" Upgrades found and queued: {upgrades_found}")
|
|
print(f" Already at best quality: {already_best}")
|
|
print(f" Errors: {errors}")
|
|
print(f" Total checked: {len(attachments)}")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
asyncio.run(check_and_upgrade_attachments())
|