Add shotscraper script
This commit is contained in:
parent
eca8876d09
commit
3c1a347fa3
1 changed files with 98 additions and 0 deletions
98
shotscraper
Executable file
98
shotscraper
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# dependencies = []
|
||||
# ///
|
||||
"""
|
||||
shot-scraper wrapper that uses Firefox cookies via cookiefire.
|
||||
|
||||
Usage:
|
||||
shotscraper https://example.com -o out.png
|
||||
shotscraper -P jtm https://private.com -o private.png
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
def parse_netscape_cookies(cookie_text):
|
||||
"""Convert Netscape cookie format to Playwright storage_state format."""
|
||||
cookies = []
|
||||
for line in cookie_text.strip().split('\n'):
|
||||
if line.startswith('#') or not line.strip():
|
||||
continue
|
||||
parts = line.split('\t')
|
||||
if len(parts) >= 7:
|
||||
domain, _, path, secure, expires, name, value = parts[:7]
|
||||
# playwright requires -1 (session) or positive unix timestamp in seconds
|
||||
# firefox stores milliseconds, so divide by 1000
|
||||
exp = float(expires)
|
||||
if exp <= 0:
|
||||
exp = -1
|
||||
elif exp > 9999999999: # looks like milliseconds
|
||||
exp = exp / 1000
|
||||
cookies.append({
|
||||
"name": name,
|
||||
"value": value,
|
||||
"domain": domain,
|
||||
"path": path,
|
||||
"expires": exp,
|
||||
"httpOnly": False,
|
||||
"secure": secure.upper() == "TRUE",
|
||||
"sameSite": "Lax",
|
||||
})
|
||||
return {"cookies": cookies, "origins": []}
|
||||
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
profile = "jtm"
|
||||
passthrough = []
|
||||
|
||||
# extract -P profile from args
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i] == "-P" and i + 1 < len(args):
|
||||
next_arg = args[i + 1]
|
||||
# don't treat URLs as profile names
|
||||
if next_arg.startswith("http://") or next_arg.startswith("https://"):
|
||||
passthrough.append(next_arg)
|
||||
i += 2
|
||||
else:
|
||||
profile = next_arg
|
||||
i += 2
|
||||
else:
|
||||
passthrough.append(args[i])
|
||||
i += 1
|
||||
|
||||
# get cookies from firefox
|
||||
try:
|
||||
cookie_text = subprocess.check_output(
|
||||
["cookiefire", profile],
|
||||
text=True
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"cookiefire failed: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
storage_state = parse_netscape_cookies(cookie_text)
|
||||
|
||||
# write to temp file
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode='w', suffix='.json', delete=False
|
||||
) as f:
|
||||
json.dump(storage_state, f)
|
||||
auth_file = f.name
|
||||
|
||||
try:
|
||||
cmd = ["shot-scraper", "--auth", auth_file] + passthrough
|
||||
result = subprocess.run(cmd)
|
||||
sys.exit(result.returncode)
|
||||
finally:
|
||||
os.unlink(auth_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in a new issue