summaryrefslogtreecommitdiff
path: root/youtube_dl/cache.py
blob: 4822439d0ed07b75be95034f004d3169a171738b (plain)
    1 from __future__ import unicode_literals
    2 
    3 import errno
    4 import io
    5 import json
    6 import os
    7 import re
    8 import shutil
    9 import traceback
   10 
   11 from .compat import compat_getenv
   12 from .utils import (
   13     error_to_compat_str,
   14     expand_path,
   15     is_outdated_version,
   16     try_get,
   17     write_json_file,
   18 )
   19 from .version import __version__
   20 
   21 
   22 class Cache(object):
   23 
   24     _YTDL_DIR = 'youtube-dl'
   25     _VERSION_KEY = _YTDL_DIR + '_version'
   26     _DEFAULT_VERSION = '2021.12.17'
   27 
   28     def __init__(self, ydl):
   29         self._ydl = ydl
   30 
   31     def _get_root_dir(self):
   32         res = self._ydl.params.get('cachedir')
   33         if res is None:
   34             cache_root = compat_getenv('XDG_CACHE_HOME', '~/.cache')
   35             res = os.path.join(cache_root, self._YTDL_DIR)
   36         return expand_path(res)
   37 
   38     def _get_cache_fn(self, section, key, dtype):
   39         assert re.match(r'^[a-zA-Z0-9_.-]+$', section), \
   40             'invalid section %r' % section
   41         assert re.match(r'^[a-zA-Z0-9_.-]+$', key), 'invalid key %r' % key
   42         return os.path.join(
   43             self._get_root_dir(), section, '%s.%s' % (key, dtype))
   44 
   45     @property
   46     def enabled(self):
   47         return self._ydl.params.get('cachedir') is not False
   48 
   49     def store(self, section, key, data, dtype='json'):
   50         assert dtype in ('json',)
   51 
   52         if not self.enabled:
   53             return
   54 
   55         fn = self._get_cache_fn(section, key, dtype)
   56         try:
   57             try:
   58                 os.makedirs(os.path.dirname(fn))
   59             except OSError as ose:
   60                 if ose.errno != errno.EEXIST:
   61                     raise
   62             write_json_file({self._VERSION_KEY: __version__, 'data': data}, fn)
   63         except Exception:
   64             tb = traceback.format_exc()
   65             self._ydl.report_warning(
   66                 'Writing cache to %r failed: %s' % (fn, tb))
   67 
   68     def _validate(self, data, min_ver):
   69         version = try_get(data, lambda x: x[self._VERSION_KEY])
   70         if not version:  # Backward compatibility
   71             data, version = {'data': data}, self._DEFAULT_VERSION
   72         if not is_outdated_version(version, min_ver or '0', assume_new=False):
   73             return data['data']
   74         self._ydl.to_screen(
   75             'Discarding old cache from version {version} (needs {min_ver})'.format(**locals()))
   76 
   77     def load(self, section, key, dtype='json', default=None, min_ver=None):
   78         assert dtype in ('json',)
   79 
   80         if not self.enabled:
   81             return default
   82 
   83         cache_fn = self._get_cache_fn(section, key, dtype)
   84         try:
   85             try:
   86                 with io.open(cache_fn, 'r', encoding='utf-8') as cachef:
   87                     return self._validate(json.load(cachef), min_ver)
   88             except ValueError:
   89                 try:
   90                     file_size = os.path.getsize(cache_fn)
   91                 except (OSError, IOError) as oe:
   92                     file_size = error_to_compat_str(oe)
   93                 self._ydl.report_warning(
   94                     'Cache retrieval from %s failed (%s)' % (cache_fn, file_size))
   95         except IOError:
   96             pass  # No cache available
   97 
   98         return default
   99 
  100     def remove(self):
  101         if not self.enabled:
  102             self._ydl.to_screen('Cache is disabled (Did you combine --no-cache-dir and --rm-cache-dir?)')
  103             return
  104 
  105         cachedir = self._get_root_dir()
  106         if not any((term in cachedir) for term in ('cache', 'tmp')):
  107             raise Exception('Not removing directory %s - this does not look like a cache dir' % cachedir)
  108 
  109         self._ydl.to_screen(
  110             'Removing cache dir %s .' % cachedir, skip_eol=True)
  111         if os.path.exists(cachedir):
  112             self._ydl.to_screen('.', skip_eol=True)
  113             shutil.rmtree(cachedir)
  114         self._ydl.to_screen('.')

Generated by cgit