Kodi plugin to to play various online streams (mostly Latvian)

storage.py 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. # -*- coding: utf-8 -*-
  2. """
  3. kodiswift.storage
  4. -----------------
  5. This module contains persistent storage classes.
  6. :copyright: (c) 2012 by Jonathan Beluch
  7. :license: GPLv3, see LICENSE for more details.
  8. """
  9. from __future__ import absolute_import
  10. import collections
  11. import json
  12. import os
  13. import time
  14. import shutil
  15. from datetime import datetime
  16. try:
  17. import cPickle as pickle
  18. except ImportError:
  19. import pickle
  20. __all__ = ['Formats', 'PersistentStorage', 'TimedStorage', 'UnknownFormat']
  21. class UnknownFormat(Exception):
  22. pass
  23. class Formats(object):
  24. PICKLE = 'pickle'
  25. JSON = 'json'
  26. class PersistentStorage(collections.MutableMapping):
  27. def __init__(self, file_path, file_format=Formats.PICKLE):
  28. """
  29. Args:
  30. file_path (str):
  31. file_format (Optional[kodiswift.Formats]):
  32. """
  33. super(PersistentStorage, self).__init__()
  34. self.file_path = file_path
  35. self.file_format = file_format
  36. self._store = {}
  37. self._loaded = False
  38. def __getitem__(self, key):
  39. return self._store[key]
  40. def __setitem__(self, key, value):
  41. self._store[key] = value
  42. def __delitem__(self, key):
  43. del self._store[key]
  44. def __iter__(self):
  45. return iter(self._store)
  46. def __len__(self):
  47. return len(self._store)
  48. def __enter__(self):
  49. self.load()
  50. self.sync()
  51. return self
  52. def __exit__(self, exc_type, exc_val, exc_tb):
  53. self.sync()
  54. def __repr__(self):
  55. return '%s(%r)' % (self.__class__.__name__, self._store)
  56. def items(self):
  57. return self._store.items()
  58. def load(self):
  59. """Load the file from disk.
  60. Returns:
  61. bool: True if successfully loaded, False if the file
  62. doesn't exist.
  63. Raises:
  64. UnknownFormat: When the file exists but couldn't be loaded.
  65. """
  66. if not self._loaded and os.path.exists(self.file_path):
  67. with open(self.file_path, 'rb') as f:
  68. for loader in (pickle.load, json.load):
  69. try:
  70. f.seek(0)
  71. self._store = loader(f)
  72. self._loaded = True
  73. break
  74. except pickle.UnpicklingError:
  75. pass
  76. # If the file exists and wasn't able to be loaded, raise an error.
  77. if not self._loaded:
  78. raise UnknownFormat('Failed to load file')
  79. return self._loaded
  80. def close(self):
  81. self.sync()
  82. def sync(self):
  83. temp_file = self.file_path + '.tmp'
  84. try:
  85. with open(temp_file, 'wb') as f:
  86. if self.file_format == Formats.PICKLE:
  87. pickle.dump(self._store, f, 2)
  88. elif self.file_format == Formats.JSON:
  89. json.dump(self._store, f, separators=(',', ':'))
  90. else:
  91. raise NotImplementedError(
  92. 'Unknown file format ' + repr(self.file_format))
  93. except Exception:
  94. if os.path.exists(temp_file):
  95. os.remove(temp_file)
  96. raise
  97. shutil.move(temp_file, self.file_path)
  98. class TimedStorage(PersistentStorage):
  99. """A dict with the ability to persist to disk and TTL for items."""
  100. def __init__(self, file_path, ttl=None, **kwargs):
  101. """
  102. Args:
  103. file_path (str):
  104. ttl (Optional[int]):
  105. """
  106. super(TimedStorage, self).__init__(file_path, **kwargs)
  107. self.ttl = ttl
  108. def __setitem__(self, key, value):
  109. self._store[key] = (value, time.time())
  110. def __getitem__(self, item):
  111. val, timestamp = self._store[item]
  112. ttl_diff = datetime.utcnow() - datetime.utcfromtimestamp(timestamp)
  113. if self.ttl and ttl_diff > self.ttl:
  114. del self._store[item]
  115. raise KeyError
  116. return val
  117. def __repr__(self):
  118. return '%s(%r)' % (self.__class__.__name__,
  119. dict((k, v[0]) for k, v in self._store.items()))
  120. def items(self):
  121. items = []
  122. for k in self._store.keys():
  123. try:
  124. items.append((k, self[k]))
  125. except KeyError:
  126. pass
  127. return items
  128. def sync(self):
  129. super(TimedStorage, self).sync()