yt-dlp-dags/ytops_client/cookie_tool.py

140 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""
Tool to convert JSON cookies to the standard Netscape txt format.
"""
import argparse
import json
import sys
import logging
# Configure logging
logger = logging.getLogger('cookie_tool')
def convert_json_to_netscape(json_data):
"""
Converts a list of cookie dictionaries to a Netscape format string.
"""
netscape_cookies = []
# The header is optional but good practice for some tools.
netscape_cookies.append("# Netscape HTTP Cookie File")
netscape_cookies.append("# http://www.netscape.com/newsref/std/cookie_spec.html")
netscape_cookies.append("# This is a generated file! Do not edit.")
netscape_cookies.append("")
if not isinstance(json_data, list):
raise TypeError("Input JSON must be a list of cookie objects.")
for cookie in json_data:
if not isinstance(cookie, dict):
logger.warning(f"Skipping non-dictionary item in JSON list: {cookie}")
continue
domain = cookie.get('domain', '')
# The 'hostOnly' flag determines if the domain is accessible to subdomains.
# Netscape format's flag is TRUE if subdomains can access it.
# So, hostOnly=false means flag=TRUE.
# A leading dot in the domain also implies this for some implementations.
if domain.startswith('.'):
include_subdomains = 'TRUE'
else:
include_subdomains = 'FALSE' if cookie.get('hostOnly', True) else 'TRUE'
path = cookie.get('path', '/')
secure = 'TRUE' if cookie.get('secure', False) else 'FALSE'
# Expiration date. If session cookie or no expiration, use 0.
if cookie.get('session', False) or 'expirationDate' not in cookie or cookie['expirationDate'] is None:
expires = 0
else:
expires = int(cookie['expirationDate'])
name = cookie.get('name', '')
value = str(cookie.get('value', ''))
# Skip cookies without essential fields
if not domain or not name:
logger.warning(f"Skipping cookie with missing domain or name: {cookie}")
continue
netscape_cookies.append(
f"{domain}\t{include_subdomains}\t{path}\t{secure}\t{expires}\t{name}\t{value}"
)
return "\n".join(netscape_cookies)
def add_cookie_tool_parser(subparsers):
"""Add the parser for the 'convert-cookies' command."""
parser = subparsers.add_parser(
'convert-cookies',
description='Convert JSON cookies to Netscape format.',
formatter_class=argparse.RawTextHelpFormatter,
help='Convert JSON cookies to Netscape format.',
epilog="""
Reads a JSON array of cookie objects from stdin and prints the
Netscape cookie file format to stdout.
Example JSON input format (per cookie):
{
"domain": ".example.com",
"hostOnly": false,
"path": "/",
"secure": true,
"expirationDate": 1672531199,
"name": "my_cookie",
"value": "my_value"
}
Example usage:
cat cookies.json | yt-ops-client convert-cookies > cookies.txt
"""
)
parser.add_argument(
'input_file',
nargs='?',
type=argparse.FileType('r', encoding='utf-8'),
default=sys.stdin,
help="Path to the JSON cookie file. Reads from stdin if not provided."
)
parser.add_argument(
'-o', '--output',
type=argparse.FileType('w', encoding='utf-8'),
default=sys.stdout,
help="Output file path for the Netscape cookies. Defaults to stdout."
)
parser.add_argument('--verbose', action='store_true', help='Enable verbose logging.')
return parser
def main_cookie_tool(args):
"""Main logic for the 'convert-cookies' command."""
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s', stream=sys.stderr)
else:
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s', stream=sys.stderr)
try:
json_content = args.input_file.read()
if not json_content.strip():
logger.error("Input is empty.")
return 1
cookie_data = json.loads(json_content)
netscape_string = convert_json_to_netscape(cookie_data)
args.output.write(netscape_string + '\n')
if args.output is not sys.stdout:
logger.info(f"Successfully converted cookies to {args.output.name}")
return 0
except json.JSONDecodeError:
logger.error("Invalid JSON provided. Please check the input file.")
return 1
except TypeError as e:
logger.error(f"Error processing JSON: {e}")
return 1
except Exception as e:
logger.error(f"An unexpected error occurred: {e}", exc_info=args.verbose)
return 1