Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

125 rindas
3.7 KiB

  1. """ Runner for the Lo-Fi Beats bot at https://botsin.space/@lofibeats """
  2. import html
  3. import logging
  4. import logging.handlers
  5. import os
  6. import pickle
  7. import random
  8. import isodate
  9. import mastodon
  10. import youtube
  11. logging.basicConfig(
  12. handlers=[logging.handlers.RotatingFileHandler('beatbot.log', maxBytes=100000, backupCount=10),
  13. logging.StreamHandler()],
  14. level=logging.INFO,
  15. format="[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s",
  16. datefmt='%Y-%m-%dT%H:%M:%S')
  17. LOGGER = logging.getLogger(__name__)
  18. def randline(fname):
  19. """ Get a random line from a file """
  20. with open(fname) as afile:
  21. line = next(afile)
  22. for num, aline in enumerate(afile, 2):
  23. if random.randrange(num) == 0:
  24. line = aline
  25. return line.strip()
  26. def format_details(yt_id, snippet):
  27. ''' format the details of a video '''
  28. text = f'https://youtube.com/watch?v={yt_id}'
  29. try:
  30. LOGGER.info("snippet: %s", snippet)
  31. if 'liveBroadcastContent' in snippet and snippet['liveBroadcastContent'] == 'live':
  32. duration = ' (LIVE)'
  33. else:
  34. duration = ''
  35. details = youtube.get_details(yt_id)
  36. LOGGER.info("details: %s", details)
  37. if details and 'duration' in details:
  38. delta = isodate.parse_duration(details['duration'])
  39. hours, rem = divmod(delta.seconds, 3600)
  40. minutes, seconds = divmod(rem, 60)
  41. if hours:
  42. duration = f' ({hours:d}:{minutes:02d}:{seconds:02d})'
  43. elif minutes or seconds:
  44. duration = f' ({minutes:d}:{seconds:02d})'
  45. text += f' — {html.unescape(snippet["title"])}{duration}'
  46. except RuntimeError:
  47. LOGGER.exception("Unable to get video details")
  48. return text
  49. def bot():
  50. """ Run the bot """
  51. genre = randline('genres.txt')
  52. verb1 = randline('1syllableverbs.txt')
  53. verb2 = randline('2syllableverbs.txt')
  54. search_term = f'+lofi {genre} +beats {verb1} {verb2}'
  55. text = f"lo-fi {genre} beats to {verb1} and {verb2} to".replace(' ', ' ')
  56. seen_vids = set()
  57. try:
  58. with open('seen-vids.dat', 'rb') as seen:
  59. seen_vids = pickle.load(seen)
  60. except FileNotFoundError:
  61. LOGGER.info("Seen database not found")
  62. except pickle.UnpicklingError:
  63. LOGGER.exception("Seen database corrupted")
  64. videos = youtube.get_videos(search_term)
  65. LOGGER.info("%s: Found %d videos (%d new)", search_term,
  66. len(videos), len({id for id, _ in videos} - seen_vids))
  67. yt_id = None
  68. random.shuffle(videos)
  69. # try to find a random video we haven't used before
  70. for (vid, snippet) in videos:
  71. if vid not in seen_vids:
  72. yt_id = vid
  73. LOGGER.info("Using [https://youtube.com/watch?v=%s] %s",
  74. yt_id, snippet['title'])
  75. break
  76. if not yt_id and videos:
  77. # nothing new, so just select a random one (list is already shuffled)
  78. (yt_id, snippet) = videos[0]
  79. LOGGER.info("Reusing [https://youtube.com/watch?v=%s] %s",
  80. yt_id, snippet['title'])
  81. if yt_id:
  82. text += '\n\n' + format_details(yt_id, snippet)
  83. # remember that we used it already
  84. seen_vids.add(yt_id)
  85. LOGGER.info(text)
  86. if not os.environ.get('BEATBOT_TESTING'):
  87. mdon = mastodon.Mastodon(
  88. access_token='token.secret',
  89. api_base_url='https://botsin.space')
  90. mdon.status_post(text)
  91. with open('seen-vids.dat', 'wb') as seen:
  92. pickle.dump(seen_vids, seen)
  93. if __name__ == '__main__':
  94. try:
  95. bot()
  96. except Exception:
  97. LOGGER.exception("Uncaught exception")
  98. raise