lofibeats/beatbot.py

134 rader
4.1 KiB
Python
Permalänk Normal vy Historik

2021-05-01 09:54:06 -07:00
""" Runner for the Lo-Fi Beats bot at https://botsin.space/@lofibeats """
2021-05-11 09:58:20 -07:00
import html
2021-04-29 09:58:32 -07:00
import logging
import logging.handlers
import os
2021-04-29 09:58:32 -07:00
import pickle
2020-06-23 11:37:20 -07:00
import random
2021-04-29 09:58:32 -07:00
2021-05-11 09:58:20 -07:00
import isodate
2021-04-29 09:58:32 -07:00
import mastodon
2021-04-30 10:53:01 -07:00
import youtube
2021-04-29 09:58:32 -07:00
logging.basicConfig(
handlers=[logging.handlers.RotatingFileHandler('beatbot.log', maxBytes=100000, backupCount=10),
logging.StreamHandler()],
2021-04-29 09:58:32 -07:00
level=logging.INFO,
format="[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s",
datefmt='%Y-%m-%dT%H:%M:%S')
LOGGER = logging.getLogger(__name__)
2020-06-23 11:37:20 -07:00
def randline(fname):
2021-05-01 09:54:06 -07:00
""" Get a random line from a file """
2022-06-08 10:02:13 -07:00
with open(fname, encoding='utf-8') as afile:
2020-06-23 11:37:20 -07:00
line = next(afile)
for num, aline in enumerate(afile, 2):
if random.randrange(num) == 0:
line = aline
return line.strip()
2021-04-29 09:58:32 -07:00
2021-05-09 20:39:43 -07:00
def format_details(yt_id, snippet):
''' format the details of a video '''
text = f'https://youtube.com/watch?v={yt_id}'
try:
LOGGER.info("snippet: %s", snippet)
if 'liveBroadcastContent' in snippet and snippet['liveBroadcastContent'] == 'live':
2021-05-09 20:39:43 -07:00
duration = ' (LIVE)'
else:
duration = ''
details = youtube.get_details(yt_id)
LOGGER.info("details: %s", details)
if details and 'duration' in details:
2021-05-09 20:39:43 -07:00
delta = isodate.parse_duration(details['duration'])
hours, rem = divmod(delta.seconds, 3600)
minutes, seconds = divmod(rem, 60)
if hours:
2021-05-11 09:58:20 -07:00
duration = f' ({hours:d}:{minutes:02d}:{seconds:02d})'
2021-05-15 02:22:37 -07:00
elif minutes or seconds:
2021-05-11 09:58:20 -07:00
duration = f' ({minutes:d}:{seconds:02d})'
text += f'{html.unescape(snippet["title"])}{duration}'
2021-05-09 20:39:43 -07:00
except RuntimeError:
LOGGER.exception("Unable to get video details")
return text
2021-05-11 09:58:20 -07:00
2020-06-23 11:37:20 -07:00
def bot():
2021-05-01 09:54:06 -07:00
""" Run the bot """
seen_vids = set()
2021-04-28 18:11:06 -07:00
try:
with open('seen-vids.dat', 'rb') as seen:
seen_vids = pickle.load(seen)
2021-05-01 09:54:06 -07:00
except FileNotFoundError:
LOGGER.info("Seen database not found")
except pickle.UnpicklingError:
LOGGER.exception("Seen database corrupted")
2023-04-26 13:35:34 -07:00
for _ in range(5):
genre = randline('genres.txt')
noun = randline('nouns.txt')
verb1 = randline('1syllableverbs.txt')
verb2 = randline('2syllableverbs.txt')
search_term = f'+lofi {genre} +{noun} {verb1} {verb2}'
text = f"lo-fi {genre} {noun} to {verb1} and {verb2} to".replace(' ', ' ')
2023-04-26 13:35:34 -07:00
region, topic = randline('yt_topics.txt').split()
LOGGER.info("Searching in region %s (topicId=%s)", region, topic)
videos = youtube.get_videos(search_term, regionCode=region, topicId=topic,
safeSearch="strict")
LOGGER.info("%s: Found %d videos (%d new)", search_term,
len(videos), len({id for id, _ in videos} - seen_vids))
if videos:
break
2021-04-29 09:58:32 -07:00
2021-04-28 18:11:06 -07:00
yt_id = None
2021-05-06 23:03:57 -07:00
random.shuffle(videos)
2021-04-28 18:11:06 -07:00
# try to find a random video we haven't used before
2021-05-09 20:39:43 -07:00
for (vid, snippet) in videos:
2021-04-28 18:11:06 -07:00
if vid not in seen_vids:
yt_id = vid
2021-05-09 20:39:43 -07:00
LOGGER.info("Using [https://youtube.com/watch?v=%s] %s",
2021-05-11 09:58:20 -07:00
yt_id, snippet['title'])
2021-04-28 18:11:06 -07:00
break
2021-05-06 23:03:57 -07:00
if not yt_id and videos:
2021-04-28 18:11:06 -07:00
# nothing new, so just select a random one (list is already shuffled)
2021-05-09 20:39:43 -07:00
(yt_id, snippet) = videos[0]
LOGGER.info("Reusing [https://youtube.com/watch?v=%s] %s",
2021-05-11 09:58:20 -07:00
yt_id, snippet['title'])
2021-04-28 18:11:06 -07:00
if yt_id:
2021-05-09 20:39:43 -07:00
text += '\n\n' + format_details(yt_id, snippet)
2021-04-28 18:11:06 -07:00
# remember that we used it already
seen_vids.add(yt_id)
2021-04-28 17:23:30 -07:00
2021-04-29 09:58:32 -07:00
LOGGER.info(text)
2021-04-27 18:47:24 -07:00
2021-05-01 11:47:04 -07:00
if not os.environ.get('BEATBOT_TESTING'):
2021-05-01 09:54:06 -07:00
mdon = mastodon.Mastodon(
access_token='token.secret',
api_base_url='https://botsin.space')
mdon.status_post(text)
2021-05-09 22:07:52 -07:00
with open('seen-vids.dat', 'wb') as seen:
pickle.dump(seen_vids, seen)
2020-06-23 11:37:20 -07:00
2021-05-11 09:58:20 -07:00
2020-06-23 11:37:20 -07:00
if __name__ == '__main__':
2021-05-01 18:56:36 -07:00
try:
bot()
2021-05-09 20:39:43 -07:00
except Exception:
2021-05-01 18:56:36 -07:00
LOGGER.exception("Uncaught exception")
2021-05-09 20:39:43 -07:00
raise