symuid-import-cmus 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import datetime as dt
  4. import os
  5. import re
  6. import symuid
  7. # import symuid.library.cmus
  8. import sys
  9. FILE_PREFIX = b'CTC'
  10. VERSION_LENGTH = 1
  11. SUPPORTED_VERSION = b'\x0c'
  12. # always big endian, see cache_init()
  13. FLAGS_BYTEORDER = 'big'
  14. FLAGS_LENGTH = 4
  15. FLAG_64_BIT = 0x01
  16. class Track:
  17. def __init__(self, cache_size, cache_bytes):
  18. """
  19. struct cache_entry {
  20. // size of this struct including size itself
  21. uint32_t size;
  22. int32_t play_count;
  23. int64_t mtime;
  24. int32_t duration;
  25. int32_t bitrate;
  26. int32_t bpm;
  27. uint8_t _reserved[CACHE_ENTRY_RESERVED_SIZE];
  28. // filename, codec, codec_profile and N * (key, val)
  29. char strings[];
  30. };
  31. """
  32. assert len(cache_bytes) + 4 == cache_size
  33. self._play_count = int.from_bytes(cache_bytes[0:4], byteorder=sys.byteorder)
  34. self._path = cache_bytes.split(b'\xff' * 56)[1].split(b'\x00')[0]
  35. if self._play_count > 0:
  36. print(self._play_count, self._path.decode())
  37. def symuid_import_cmus(cache_path):
  38. with open(os.path.expanduser(cache_path), 'rb') as cache:
  39. # see cache.c cache_init()
  40. assert cache.read(len(FILE_PREFIX)) == FILE_PREFIX
  41. cache_version = cache.read(VERSION_LENGTH)
  42. assert cache_version == SUPPORTED_VERSION, cache_version
  43. flags = int.from_bytes(cache.read(FLAGS_LENGTH), byteorder=FLAGS_BYTEORDER)
  44. # only support 64-bit flag
  45. assert flags & ~FLAG_64_BIT == 0, flags
  46. # size includes size itself
  47. while True:
  48. size = int.from_bytes(cache.read(4), byteorder=sys.byteorder)
  49. if size == 0:
  50. break
  51. Track(size, cache.read(size - 4))
  52. # see cache.c write_ti ALIGN
  53. cache.read((-size) % 8)
  54. def _init_argparser():
  55. import argparse
  56. argparser = argparse.ArgumentParser(description=None)
  57. argparser.add_argument(
  58. 'cache_path',
  59. nargs='?',
  60. default='~/.config/cmus/cache',
  61. help='(default: %(default)r)',
  62. )
  63. return argparser
  64. def main(argv):
  65. argparser = _init_argparser()
  66. args = argparser.parse_args(argv[1:])
  67. symuid_import_cmus(**vars(args))
  68. return 0
  69. if __name__ == "__main__":
  70. sys.exit(main(sys.argv))