yt_dlp/devscripts/utils.py

118 lines
3.5 KiB
Python
Raw Normal View History

from __future__ import annotations
import argparse
import contextlib
import datetime as dt
import functools
import itertools
import json
import os
import re
2023-03-04 22:40:08 +05:30
import subprocess
import urllib.parse
import urllib.request
import zipfile
def read_file(fname):
with open(fname, encoding='utf-8') as f:
return f.read()
def write_file(fname, content, mode='w'):
with open(fname, mode, encoding='utf-8') as f:
return f.write(content)
def read_version(fname='yt_dlp/version.py', varname='__version__'):
2023-03-04 22:40:08 +05:30
"""Get the version without importing the package"""
items = {}
exec(compile(read_file(fname), fname, 'exec'), items)
return items[varname]
def calculate_version(version=None, fname='yt_dlp/version.py'):
if version and '.' in version:
return version
revision = version
version = dt.datetime.now(dt.timezone.utc).strftime('%Y.%m.%d')
if revision:
assert re.fullmatch(r'[0-9]+', revision), 'Revision must be numeric'
else:
old_version = read_version(fname=fname).split('.')
if version.split('.') == old_version[:3]:
revision = str(int(([*old_version, 0])[3]) + 1)
return f'{version}.{revision}' if revision else version
def get_filename_args(has_infile=False, default_outfile=None):
parser = argparse.ArgumentParser()
if has_infile:
parser.add_argument('infile', help='Input file')
kwargs = {'nargs': '?', 'default': default_outfile} if default_outfile else {}
parser.add_argument('outfile', **kwargs, help='Output file')
opts = parser.parse_args()
if has_infile:
return opts.infile, opts.outfile
return opts.outfile
def compose_functions(*functions):
return lambda x: functools.reduce(lambda y, f: f(y), functions, x)
2023-03-04 22:40:08 +05:30
def run_process(*args, **kwargs):
kwargs.setdefault('text', True)
kwargs.setdefault('check', True)
kwargs.setdefault('capture_output', True)
if kwargs['text']:
kwargs.setdefault('encoding', 'utf-8')
kwargs.setdefault('errors', 'replace')
return subprocess.run(args, **kwargs)
def request(url: str, *, headers: dict | None = None):
req = urllib.request.Request(url, headers=headers or {})
return contextlib.closing(urllib.request.urlopen(req))
def call_github_api(path: str, *, query: dict | None = None) -> dict | list:
API_BASE_URL = 'https://api.github.com/'
assert not path.startswith(('https://', 'http://')) or path.startswith(API_BASE_URL)
url = urllib.parse.urlparse(urllib.parse.urljoin(API_BASE_URL, path))
qs = urllib.parse.urlencode({
**urllib.parse.parse_qs(url.query),
**(query or {}),
}, True)
headers = {
'Accept': 'application/vnd.github+json',
'User-Agent': 'yt-dlp',
'X-GitHub-Api-Version': '2026-03-10',
}
if gh_token := os.getenv('GH_TOKEN'):
headers['Authorization'] = f'Bearer {gh_token}'
with request(urllib.parse.urlunparse(url._replace(query=qs)), headers=headers) as resp:
return json.load(resp)
def zipf_files_and_folders(zipf: zipfile.ZipFile, glob: str = '*') -> tuple[list[str], list[str]]:
files = []
folders = []
path = zipfile.Path(zipf)
for f in itertools.chain(path.glob(glob), path.rglob(glob)):
if not f.is_file():
continue
files.append(f.at) # type: ignore[attr-defined]
folder = f.parent.at.rstrip('/') # type: ignore[attr-defined]
if folder and folder not in folders:
folders.append(folder)
return files, folders