indexer.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import time
  2. from queue import Queue
  3. from threading import Thread, Lock
  4. import vlc
  5. from path import Path
  6. from core.exceptions import NotSupportedFile
  7. from core.file_utilities import is_media_file_ext, hash_file
  8. from core.logging_ import Logger
  9. from core.models import Track
  10. from core.repositories import MusicFolderRepository, TrackRepository
  11. logger = Logger.get()
  12. class CyclicThread(Thread):
  13. DELAY = 0
  14. def __init__(self):
  15. Thread.__init__(self)
  16. self.interrupted = False
  17. self.last_exec = 0
  18. def act(self):
  19. raise NotImplementedError()
  20. def run(self):
  21. t = None
  22. while 1:
  23. if self.DELAY:
  24. t = time.time()
  25. if not self.DELAY or not self.last_exec or (t - self.last_exec) > self.DELAY:
  26. self.act()
  27. self.last_exec = t
  28. if self.interrupted:
  29. break
  30. def trigger(self):
  31. self.last_exec = 0
  32. def stop(self):
  33. self.interrupted = True
  34. class Discoverer(CyclicThread):
  35. DELAY = 5
  36. def __init__(self, indexer):
  37. CyclicThread.__init__(self)
  38. self.indexer = indexer
  39. def act(self):
  40. music_folder_repo = MusicFolderRepository()
  41. track_repo = TrackRepository()
  42. index = {t.path: t for t in track_repo.get_all()}
  43. for music_folder in music_folder_repo.get_all():
  44. music_folder_path = Path(music_folder.path)
  45. for filename in music_folder_path.walkfiles():
  46. if filename not in index and is_media_file_ext(filename.ext):
  47. self.indexer.put(filename)
  48. elif filename in index:
  49. track = index[filename]
  50. if track.status == Track.STATUS_UNAVAILABLE:
  51. self.indexer.put(track.id)
  52. del index[filename]
  53. for filename, track in index.items():
  54. filename = Path(filename)
  55. if not filename.exists():
  56. self.indexer.put(track.id)
  57. class Indexer(CyclicThread):
  58. def __init__(self):
  59. CyclicThread.__init__(self)
  60. self.queue = Queue()
  61. self.interrupted = False
  62. self.discoverer = Discoverer(self)
  63. def start(self):
  64. logger.info('** indexation thread started **')
  65. self.discoverer.start()
  66. super().start()
  67. def act(self):
  68. if not self.queue.empty():
  69. filename = self.queue.get(False)
  70. try:
  71. self.index(filename)
  72. except (FileNotFoundError, NotSupportedFile) as e:
  73. logger.warning("Error during indexation: %s" % e)
  74. def put(self, filename):
  75. self.queue.put(filename)
  76. @staticmethod
  77. def index(filename_or_track_id):
  78. """ index a media file from the filesystem or a track id """
  79. track_repo = TrackRepository()
  80. if type(filename_or_track_id) is int:
  81. track = track_repo.get_by_id(filename_or_track_id)
  82. filename = Path(track.path)
  83. track_hash = track.hash
  84. if not filename.exists():
  85. logger.debug('Index - missing: %s' % filename)
  86. track.status = Track.STATUS_UNAVAILABLE
  87. track_repo.commit()
  88. return
  89. else:
  90. filename = Path(filename_or_track_id)
  91. if not filename.exists():
  92. raise FileNotFoundError(f"File not found: {filename}")
  93. if not is_media_file_ext(filename.ext):
  94. raise NotSupportedFile(f"File's extension {filename.ext} is not supported")
  95. track_hash = hash_file(filename)
  96. track = track_repo.get_by_hash(track_hash)
  97. if not track:
  98. track = Track()
  99. vlc_media = vlc.Media(filename)
  100. vlc_media.parse()
  101. track_infos = vlc_media.get_tracks_info()
  102. track.title = vlc_media.get_meta(vlc.Meta.Title) or filename.stripext().name
  103. track.format = filename.ext
  104. track.artist = vlc_media.get_meta(vlc.Meta.AlbumArtist) or vlc_media.get_meta(vlc.Meta.Artist)
  105. track.album = vlc_media.get_meta(vlc.Meta.Album)
  106. track.track_num = vlc_media.get_meta(vlc.Meta.TrackNumber)
  107. # track.year = vlc_media.get_meta(vlc.Meta.Date)
  108. # track.duration = vlc_media.get_meta(vlc.Meta.Date)
  109. # track.size = 0
  110. track.note = ""
  111. track.status = Track.STATUS_FOUND
  112. track.path = filename
  113. track.hash = track_hash
  114. if track.id is None:
  115. track_repo.create(track)
  116. logger.debug('Index - updated: %s' % filename)
  117. track_repo.commit()
  118. def stop(self):
  119. self.discoverer.stop()
  120. super().stop()
  121. logger.info('** indexation thread stopped **')
  122. if __name__ == '__main__':
  123. indexer = Indexer()
  124. indexer.start()
  125. try:
  126. indexer.join()
  127. except KeyboardInterrupt:
  128. indexer.stop()