mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-02-19 22:30:49 +08:00
Merge pull request #26 from H3CoF6/feat/sns-reverse
feat (sns): calc img path for sns
This commit is contained in:
@@ -609,10 +609,17 @@ const getSnsMediaUrl = (post, m, idx, rawUrl) => {
|
|||||||
if (h) parts.set('height', h)
|
if (h) parts.set('height', h)
|
||||||
if (/^\d+$/.test(ts)) parts.set('total_size', ts)
|
if (/^\d+$/.test(ts)) parts.set('total_size', ts)
|
||||||
parts.set('idx', String(Number(sizeIdx) || 0))
|
parts.set('idx', String(Number(sizeIdx) || 0))
|
||||||
|
const pid = String(post?.id || '').trim()
|
||||||
|
if (pid) parts.set('post_id', pid)
|
||||||
|
|
||||||
|
const mid = String(m?.id || '').trim()
|
||||||
|
if (mid) parts.set('media_id', mid)
|
||||||
|
|
||||||
|
const mtype = String(m?.type || '').trim()
|
||||||
|
if (mtype) parts.set('media_type', mtype)
|
||||||
|
|
||||||
if (pick) parts.set('pick', pick)
|
if (pick) parts.set('pick', pick)
|
||||||
if (!pick && snsAvoidOtherPicked.value) {
|
if (!pick && snsAvoidOtherPicked.value) {
|
||||||
const pid = String(post?.id || '').trim()
|
|
||||||
if (pid) parts.set('post_id', pid)
|
|
||||||
parts.set('avoid_picked', '1')
|
parts.set('avoid_picked', '1')
|
||||||
parts.set('pv', String(snsMediaOverrideRev.value || '0'))
|
parts.set('pv', String(snsMediaOverrideRev.value || '0'))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from bisect import bisect_left, bisect_right
|
from bisect import bisect_left, bisect_right
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import sqlite3
|
import sqlite3
|
||||||
@@ -471,6 +472,21 @@ def _sns_cache_key_from_path(p: Path) -> str:
|
|||||||
return _normalize_hex32(key)
|
return _normalize_hex32(key)
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_sns_cache_key(tid: str, media_id: str, media_type: int = 2) -> str:
|
||||||
|
"""
|
||||||
|
公式: md5(tid_mediaId_type)
|
||||||
|
Example: 14852422213384352392_14852422213963625090_2 -> 6d479249ca5a090fab5c42c79bc56b89
|
||||||
|
"""
|
||||||
|
if not tid or not media_id:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
raw_key = f"{tid}_{media_id}_{media_type}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
return hashlib.md5(raw_key.encode("utf-8")).hexdigest()
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
def _resolve_sns_cached_image_path_by_cache_key(
|
def _resolve_sns_cached_image_path_by_cache_key(
|
||||||
*,
|
*,
|
||||||
wxid_dir: Path,
|
wxid_dir: Path,
|
||||||
@@ -944,19 +960,42 @@ def list_sns_media_candidates(
|
|||||||
|
|
||||||
@router.get("/api/sns/media", summary="获取朋友圈图片(本地缓存优先)")
|
@router.get("/api/sns/media", summary="获取朋友圈图片(本地缓存优先)")
|
||||||
async def get_sns_media(
|
async def get_sns_media(
|
||||||
account: Optional[str] = None,
|
account: Optional[str] = None,
|
||||||
create_time: int = 0,
|
create_time: int = 0,
|
||||||
width: int = 0,
|
width: int = 0,
|
||||||
height: int = 0,
|
height: int = 0,
|
||||||
total_size: int = 0,
|
total_size: int = 0,
|
||||||
idx: int = 0,
|
idx: int = 0,
|
||||||
avoid_picked: int = 0,
|
avoid_picked: int = 0,
|
||||||
post_id: Optional[str] = None,
|
post_id: Optional[str] = None,
|
||||||
pick: Optional[str] = None,
|
media_id: Optional[str] = None,
|
||||||
md5: Optional[str] = None,
|
media_type: int = 2,
|
||||||
url: Optional[str] = None,
|
pick: Optional[str] = None,
|
||||||
|
md5: Optional[str] = None,
|
||||||
|
url: Optional[str] = None,
|
||||||
):
|
):
|
||||||
account_dir = _resolve_account_dir(account)
|
account_dir = _resolve_account_dir(account)
|
||||||
|
wxid_dir = _resolve_account_wxid_dir(account_dir)
|
||||||
|
|
||||||
|
if wxid_dir and post_id and media_id:
|
||||||
|
deterministic_key = _generate_sns_cache_key(post_id, media_id, media_type)
|
||||||
|
|
||||||
|
exact_match_path = _resolve_sns_cached_image_path_by_cache_key(
|
||||||
|
wxid_dir=wxid_dir,
|
||||||
|
cache_key=deterministic_key,
|
||||||
|
create_time=0
|
||||||
|
)
|
||||||
|
|
||||||
|
if exact_match_path:
|
||||||
|
try:
|
||||||
|
payload, mtype = _read_and_maybe_decrypt_media(Path(exact_match_path), account_dir)
|
||||||
|
if payload and str(mtype or "").startswith("image/"):
|
||||||
|
resp = Response(content=payload, media_type=str(mtype or "image/jpeg"))
|
||||||
|
resp.headers["Cache-Control"] = "public, max-age=31536000" # 确定性缓存可以设置很久
|
||||||
|
resp.headers["X-SNS-Source"] = "deterministic-hash"
|
||||||
|
return resp
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# 0) User-picked cache key override (stable across candidate ordering).
|
# 0) User-picked cache key override (stable across candidate ordering).
|
||||||
pick_key = _normalize_hex32(pick)
|
pick_key = _normalize_hex32(pick)
|
||||||
|
|||||||
Reference in New Issue
Block a user