store.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import hashlib
  2. import re
  3. from pathlib import Path
  4. import logging
  5. logger = logging.getLogger()
  6. def get_fname(url):
  7. m = hashlib.md5()
  8. m.update(url.encode('utf8'))
  9. return m.hexdigest()
  10. class Store:
  11. def __init__(self, basedir: Path = None):
  12. if basedir is None:
  13. basedir = Path('~/.local/share/marxbook/bookmarks/').expanduser()
  14. self.basedir = basedir
  15. self.serializer = Serializer()
  16. def add(self, url: str, title=None, tag=[], description=''):
  17. dest = self.basedir
  18. dest.mkdir(parents=True, exist_ok=True)
  19. fname = get_fname(url)
  20. fpath = dest / fname
  21. content = self.serializer.encode(dict(
  22. url=url, title=title, tags=tag, description=description))
  23. with fpath.open('w') as buf:
  24. buf.write(content)
  25. def get(self, path: str):
  26. fpath = self.basedir / path
  27. with fpath.open() as buf:
  28. return self.serializer.decode(buf.read())
  29. def __iter__(self):
  30. for urlfile in self.basedir.glob('**/*'):
  31. if not urlfile.is_file():
  32. continue
  33. data = self.get(urlfile)
  34. ret = { 'Path': str(urlfile.relative_to(self.basedir)) }
  35. ret.update(data)
  36. yield ret
  37. def folder(self, folder: str):
  38. return Store(self.basedir / folder)
  39. HEADER_LINE = re.compile(r'^([^:]+): (.*)$')
  40. class Serializer:
  41. def __init__(self):
  42. pass
  43. def encode(self, data: dict) -> str:
  44. m = ''
  45. tags = data.pop('tags', []) # those are special!
  46. for key in data:
  47. m += '%s: %s\n' % (key.title(), str(data[key]).replace('\n', ' '))
  48. for tag in tags:
  49. m += '%s: %s\n' % ('Tag', tag)
  50. return m
  51. def decode(self, content: str) -> dict:
  52. d: dict = {'Tag': []}
  53. for num, line in enumerate(content.split('\n'), 1):
  54. if not line.strip():
  55. continue
  56. m = HEADER_LINE.match(line)
  57. if m is None:
  58. logger.error("Invalid line %d" % num)
  59. continue
  60. key, value = m.groups()
  61. key = key.title()
  62. if key == 'Tag':
  63. d[key].append(value)
  64. else:
  65. d[key] = value
  66. return d
  67. if __name__ == '__main__':
  68. import sys
  69. s = Store()
  70. # print(s.get(sys.argv[1]))
  71. for line in s.list(sys.argv[1]):
  72. print(line)