58 lines
3.0 KiB
Python
58 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
import os,re,sys,argparse,urllib.request,urllib.error
|
|
PRESETS={'glo':'https://git.backend-3.com/dev/glo'}
|
|
R,G,Y,C,Z='\033[31m','\033[32m','\033[33m','\033[36m','\033[0m'
|
|
die=lambda m,n=1:(print(f'{R}error:{Z} {m}',file=sys.stderr),sys.exit(n))
|
|
warn=lambda m:print(f'{Y}warn:{Z} {m}',file=sys.stderr)
|
|
ok=lambda m:print(f'{G}{m}{Z}')
|
|
nn=lambda s:(re.sub(r'_+','_',re.sub(r'[^a-zA-Z0-9._+-]+','_',s.replace('%20','_').replace(' ','_'))).strip('._')or'downloaded_file')
|
|
def uq(p):
|
|
if not os.path.exists(p): return p
|
|
b,e=os.path.splitext(p);i=1
|
|
while os.path.exists(f'{b}_{i}{e}'): i+=1
|
|
return f'{b}_{i}{e}'
|
|
def raw(u):
|
|
m=re.match(r'^https?://github\.com/([^/]+)/([^/]+)/blob/([^/]+)/(.*)$',u)
|
|
if m:return f'https://raw.githubusercontent.com/{m[1]}/{m[2]}/{m[3]}/{m[4]}'
|
|
m=re.match(r'^(https?://[^/]+/.+)/(src|blob)/([^/]+)/(.*)$',u)
|
|
return f'{m[1]}/raw/{m[3]}/{m[4]}' if m else u
|
|
def splitrepo(s,db='main'):
|
|
s=s[1:]
|
|
if '#' in s:s,b=s.rsplit('#',1)
|
|
else:b=db
|
|
return PRESETS.get(s,s),b
|
|
def rbase(repo,br):
|
|
repo=repo.rstrip('/')
|
|
if 'github.com/' in repo and '/blob/' not in repo and '/tree/' not in repo:
|
|
m=re.match(r'^https?://github\.com/([^/]+)/([^/]+?)/?$',repo)
|
|
if m:return f'https://raw.githubusercontent.com/{m[1]}/{m[2]}/{br}'
|
|
m=re.match(r'^(https?://[^/]+/.+?)(?:/(?:src|blob|raw|tree)/([^/]+)(?:/.*)?)?/?$',repo)
|
|
return f'{m[1]}/raw/{m[2]or br}' if m else die('could not derive raw base')
|
|
def get(u,t):
|
|
q=urllib.request.Request(u,headers={'User-Agent':'pyraw/easy'})
|
|
with urllib.request.urlopen(q,timeout=t)as r:return r.read()
|
|
def save(out,rel,b):
|
|
p=os.path.join(out,*[nn(x)for x in rel.strip('/').split('/')]);os.makedirs(os.path.dirname(p),exist_ok=True);p=uq(p)
|
|
with open(p,'wb')as f:f.write(b)
|
|
return p
|
|
ap=argparse.ArgumentParser(description='easy raw file downloader')
|
|
ap.add_argument('x',nargs='+',help='url or @repo followed by repo-relative paths')
|
|
ap.add_argument('-o','--outdir',default='.')
|
|
ap.add_argument('-b','--branch',default='main')
|
|
ap.add_argument('-t','--timeout',type=int,default=20)
|
|
a=ap.parse_args();os.makedirs(a.outdir,exist_ok=True);xs=a.x
|
|
if xs[0].startswith('@'):
|
|
repo,br=splitrepo(xs[0],a.branch);base=rbase(repo,br);files=xs[1:] or die('missing repo-relative paths')
|
|
for rel in files:
|
|
u=f'{base.rstrip("/")}/{rel.lstrip("/")}'
|
|
try:b=get(u,a.timeout);p=save(a.outdir,rel,b);ok(f'saved: {C}{p}{Z}')
|
|
except urllib.error.HTTPError as e:warn(f'http {e.code} for {u}')
|
|
except urllib.error.URLError as e:warn(f'network failure for {u}: {e.reason}')
|
|
except Exception as e:warn(str(e))
|
|
else:
|
|
for u0 in xs:
|
|
u=raw(u0);n=nn(u0.split('?',1)[0].split('#',1)[0].rstrip('/').split('/')[-1]or'downloaded_file')
|
|
try:b=get(u,a.timeout);p=save(a.outdir,n,b);ok(f'saved: {C}{p}{Z}')
|
|
except urllib.error.HTTPError as e:warn(f'http {e.code} for {u}')
|
|
except urllib.error.URLError as e:warn(f'network failure for {u}: {e.reason}')
|
|
except Exception as e:warn(str(e)) |