summaryrefslogtreecommitdiffstats
path: root/gallery_dl/util.py
diff options
context:
space:
mode:
Diffstat (limited to 'gallery_dl/util.py')
-rw-r--r--gallery_dl/util.py254
1 files changed, 176 insertions, 78 deletions
diff --git a/gallery_dl/util.py b/gallery_dl/util.py
index 13bf80e..232047c 100644
--- a/gallery_dl/util.py
+++ b/gallery_dl/util.py
@@ -21,6 +21,7 @@ import datetime
import operator
import itertools
import urllib.parse
+from http.cookiejar import Cookie
from email.utils import mktime_tz, parsedate_tz
from . import text, exception
@@ -135,6 +136,67 @@ def remove_directory(path):
pass
+def load_cookiestxt(fp):
+ """Parse a Netscape cookies.txt file and return a list of its Cookies"""
+ cookies = []
+
+ for line in fp:
+
+ line = line.lstrip()
+ # strip '#HttpOnly_'
+ if line.startswith("#HttpOnly_"):
+ line = line[10:]
+ # ignore empty lines and comments
+ if not line or line[0] in ("#", "$"):
+ continue
+ # strip trailing '\n'
+ if line[-1] == "\n":
+ line = line[:-1]
+
+ domain, domain_specified, path, secure, expires, name, value = \
+ line.split("\t")
+ if not name:
+ name = value
+ value = None
+
+ cookies.append(Cookie(
+ 0, name, value,
+ None, False,
+ domain,
+ domain_specified == "TRUE",
+ domain.startswith("."),
+ path, False,
+ secure == "TRUE",
+ None if expires == "0" or not expires else expires,
+ False, None, None, {},
+ ))
+
+ return cookies
+
+
+def save_cookiestxt(fp, cookies):
+ """Write 'cookies' in Netscape cookies.txt format to 'fp'"""
+ fp.write("# Netscape HTTP Cookie File\n\n")
+
+ for cookie in cookies:
+ if cookie.value is None:
+ name = ""
+ value = cookie.name
+ else:
+ name = cookie.name
+ value = cookie.value
+
+ fp.write("\t".join((
+ cookie.domain,
+ "TRUE" if cookie.domain.startswith(".") else "FALSE",
+ cookie.path,
+ "TRUE" if cookie.secure else "FALSE",
+ "0" if cookie.expires is None else str(cookie.expires),
+ name,
+ value,
+ )) + "\n")
+
+
def code_to_language(code, default=None):
"""Map an ISO 639-1 language code to its actual name"""
return CODES.get((code or "").lower(), default)
@@ -419,63 +481,85 @@ class Formatter():
self.format_map = self.fields[0][1]
else:
self.format_map = lambda _: format_string
- del self.result
- del self.fields
+ del self.result, self.fields
- def format_map(self, kwargs):
- """Apply 'kwargs' to the initial format_string and return its result"""
+ def format_map(self, kwdict):
+ """Apply 'kwdict' to the initial format_string and return its result"""
+ result = self.result
for index, func in self.fields:
- self.result[index] = func(kwargs)
- return "".join(self.result)
+ result[index] = func(kwdict)
+ return "".join(result)
def _field_access(self, field_name, format_spec, conversion):
- first, rest = _string.formatter_field_name_split(field_name)
+ fmt = self._parse_format_spec(format_spec, conversion)
+
+ if "|" in field_name:
+ return self._apply_list([
+ self._parse_field_name(fn)
+ for fn in field_name.split("|")
+ ], fmt)
+ else:
+ key, funcs = self._parse_field_name(field_name)
+ if funcs:
+ return self._apply(key, funcs, fmt)
+ return self._apply_simple(key, fmt)
+ @staticmethod
+ def _parse_field_name(field_name):
+ first, rest = _string.formatter_field_name_split(field_name)
funcs = []
+
for is_attr, key in rest:
if is_attr:
func = operator.attrgetter
- elif ":" in key:
- func = self._slicegetter
else:
func = operator.itemgetter
+ try:
+ if ":" in key:
+ start, _, stop = key.partition(":")
+ stop, _, step = stop.partition(":")
+ start = int(start) if start else None
+ stop = int(stop) if stop else None
+ step = int(step) if step else None
+ key = slice(start, stop, step)
+ except TypeError:
+ pass # key is an integer
+
funcs.append(func(key))
- if conversion:
- funcs.append(self.CONVERSIONS[conversion])
+ return first, funcs
- if format_spec:
- if format_spec[0] == "?":
- func = self._format_optional
- elif format_spec[0] == "L":
- func = self._format_maxlen
- elif format_spec[0] == "J":
- func = self._format_join
- elif format_spec[0] == "R":
- func = self._format_replace
- else:
- func = self._format_default
- fmt = func(format_spec)
- else:
- fmt = str
+ def _parse_format_spec(self, format_spec, conversion):
+ fmt = self._build_format_func(format_spec)
+ if not conversion:
+ return fmt
- if funcs:
- return self._apply(first, funcs, fmt)
- return self._apply_simple(first, fmt)
+ conversion = self.CONVERSIONS[conversion]
+ if fmt is format:
+ return conversion
+ else:
+ def chain(obj):
+ return fmt(conversion(obj))
+ return chain
- def _apply_simple(self, key, fmt):
- def wrap(obj):
- if key in obj:
- obj = obj[key]
- else:
- obj = self.default
- return fmt(obj)
- return wrap
+ def _build_format_func(self, format_spec):
+ if format_spec:
+ fmt = format_spec[0]
+ if fmt == "?":
+ return self._parse_optional(format_spec)
+ if fmt == "L":
+ return self._parse_maxlen(format_spec)
+ if fmt == "J":
+ return self._parse_join(format_spec)
+ if fmt == "R":
+ return self._parse_replace(format_spec)
+ return self._default_format(format_spec)
+ return format
def _apply(self, key, funcs, fmt):
- def wrap(obj):
+ def wrap(kwdict):
try:
- obj = obj[key]
+ obj = kwdict[key]
for func in funcs:
obj = func(obj)
except Exception:
@@ -483,54 +567,66 @@ class Formatter():
return fmt(obj)
return wrap
- @staticmethod
- def _slicegetter(key):
- start, _, stop = key.partition(":")
- stop, _, step = stop.partition(":")
- start = int(start) if start else None
- stop = int(stop) if stop else None
- step = int(step) if step else None
- return operator.itemgetter(slice(start, stop, step))
+ def _apply_simple(self, key, fmt):
+ def wrap(kwdict):
+ return fmt(kwdict[key] if key in kwdict else self.default)
+ return wrap
- @staticmethod
- def _format_optional(format_spec):
- def wrap(obj):
- if not obj:
- return ""
- return before + format(obj, format_spec) + after
+ def _apply_list(self, lst, fmt):
+ def wrap(kwdict):
+ for key, funcs in lst:
+ try:
+ obj = kwdict[key]
+ for func in funcs:
+ obj = func(obj)
+ if obj is not None:
+ break
+ except Exception:
+ pass
+ else:
+ obj = self.default
+ return fmt(obj)
+ return wrap
+
+ def _parse_optional(self, format_spec):
before, after, format_spec = format_spec.split("/", 2)
before = before[1:]
- return wrap
+ fmt = self._build_format_func(format_spec)
- @staticmethod
- def _format_maxlen(format_spec):
- def wrap(obj):
- obj = format(obj, format_spec)
- return obj if len(obj) <= maxlen else replacement
+ def optional(obj):
+ return before + fmt(obj) + after if obj else ""
+ return optional
+
+ def _parse_maxlen(self, format_spec):
maxlen, replacement, format_spec = format_spec.split("/", 2)
maxlen = text.parse_int(maxlen[1:])
- return wrap
+ fmt = self._build_format_func(format_spec)
- @staticmethod
- def _format_join(format_spec):
- def wrap(obj):
- obj = separator.join(obj)
- return format(obj, format_spec)
+ def mlen(obj):
+ obj = fmt(obj)
+ return obj if len(obj) <= maxlen else replacement
+ return mlen
+
+ def _parse_join(self, format_spec):
separator, _, format_spec = format_spec.partition("/")
separator = separator[1:]
- return wrap
+ fmt = self._build_format_func(format_spec)
- @staticmethod
- def _format_replace(format_spec):
- def wrap(obj):
- obj = obj.replace(old, new)
- return format(obj, format_spec)
+ def join(obj):
+ return fmt(separator.join(obj))
+ return join
+
+ def _parse_replace(self, format_spec):
old, new, format_spec = format_spec.split("/", 2)
old = old[1:]
- return wrap
+ fmt = self._build_format_func(format_spec)
+
+ def replace(obj):
+ return fmt(obj.replace(old, new))
+ return replace
@staticmethod
- def _format_default(format_spec):
+ def _default_format(format_spec):
def wrap(obj):
return format(obj, format_spec)
return wrap
@@ -565,12 +661,14 @@ class PathFormat():
self.delete = False
self.path = self.realpath = self.temppath = ""
- basedir = expand_path(
- extractor.config("base-directory", (".", "gallery-dl")))
- if os.altsep and os.altsep in basedir:
- basedir = basedir.replace(os.altsep, os.sep)
- if basedir[-1] != os.sep:
- basedir += os.sep
+ basedir = extractor._parentdir
+ if not basedir:
+ basedir = expand_path(
+ extractor.config("base-directory", (".", "gallery-dl")))
+ if os.altsep and os.altsep in basedir:
+ basedir = basedir.replace(os.altsep, os.sep)
+ if basedir[-1] != os.sep:
+ basedir += os.sep
self.basedirectory = basedir
restrict = extractor.config("path-restrict", "auto")