From 3133f24bdde0be595e2f8459b388563668e0bc2e Mon Sep 17 00:00:00 2001 From: Vitalii Lebedynskyi Date: Sun, 14 Aug 2022 22:04:08 +0300 Subject: [PATCH] Reorganized chat downloader --- .DS_Store | Bin 0 -> 6148 bytes clipper/api.py | 14 +++++++----- clipper/chat.py | 53 ++++++++++++++++++++------------------------ clipper/recorder.py | 23 ++++++++++--------- clipper/video.py | 51 +++++++++++++----------------------------- 5 files changed, 60 insertions(+), 81 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..976d4e6c4636a6baeff433a4e6edeac64b2d193c GIT binary patch literal 6148 zcmeHK%}xR_5N-jrV2m709@N0DOJoz=l4X%7uG!bnH}VO5 z9cS7?LePr`W5`T0{igHNre8PhmNCZLlc3I6nK33n5pzXoz7dS0u1L;$5V;;BW&!K7 zz@LssE7|1uj||}62^Pby-7y8Y*gt=$e29JcqhUBsYPCooqNf^xAuin!-ehnmtlX}E$^MEDDlFmAM4^U=tIiQRTu?o+E$Y&80&Fd z6A+>(irsQ$HfuENRoQ6O)2f`c4r^7}Y#gO&QS2YokI#A!PtUWL^!3fK%i%-RvTJb; z?_jJf^wEu@p^EMyHm{rK6A}Z&05Pz}4A>)2Y_IVeX=%g&G4MME@O+Spd~pguaF z!QV%W7Z6dv# {msg}") + msg = self.connection.recv(8192).decode('utf-8') if self.on_message: self.on_message(msg) except BaseException as e: diff --git a/clipper/chat.py b/clipper/chat.py index 799b48b..965a82f 100644 --- a/clipper/chat.py +++ b/clipper/chat.py @@ -1,40 +1,35 @@ import logging -import os +from datetime import datetime logger = logging.getLogger(__name__) - -def parse_msg(msg): - """Breaks a message from an IRC server into its prefix, command, and arguments. - """ - prefix = '' - trailing = [] - if not msg: - raise ValueError("Empty line.") - - if msg[0] == ':': - prefix, msg = msg[1:].split(' ', 1) - if msg.find(' :') != -1: - msg, trailing = msg.split(' :', 1) - args = msg.split() - args.append(trailing) - else: - args = msg.split() - command = args.pop(0) - return prefix, command, args +CHAT_DIVIDER = "<~|~>" class TwitchChatRecorder: - def __init__(self, api, streamer_name, recording_folder): - self.recording_folder = recording_folder - self.streamer_name = streamer_name + is_running = False + + def __init__(self, api, debug=False): + self.debug = debug self.api = api - def run(self, file_template): - file_name = os.path.join(self.recording_folder, f"{file_template}.txt", ) - with open(file_name, "w") as stream: + def run(self, streamer_name, output_file): + with open(output_file, "w") as stream: def on_message(twitch_msg): - prefix, command, args = parse_msg(twitch_msg) - stream.writelines(str(args)) + user, msg = self.parse_msg(twitch_msg) + if msg: + msg_line = f"{str(datetime.now())}{CHAT_DIVIDER}{user}{CHAT_DIVIDER}{msg}" + stream.write(msg_line) + stream.flush() - self.api.start_chat(self.streamer_name, on_message) + if self.debug: + logger.info("Chat: %s", msg_line) + + self.is_running = True + self.api.start_chat(streamer_name, on_message) + + def parse_msg(self, msg): + try: + return msg[1:].split('!')[0], msg.split(":", 2)[2] + except BaseException as e: + return None, None diff --git a/clipper/recorder.py b/clipper/recorder.py index 74a2df5..106d657 100644 --- a/clipper/recorder.py +++ b/clipper/recorder.py @@ -2,6 +2,7 @@ import logging import os import time import sys +import threading from datetime import datetime from clipper.api import TwitchApi, TwitchStreamStatus @@ -27,24 +28,26 @@ class Recorder: def __init__(self, config): self.config = config self.api = TwitchApi(config.tw_client, config.tw_secret) - self.recording_folder = os.path.join(self.config.output_folder, self.config.tw_streamer) - self.video_recorder = TwitchVideoRecorder(self.api, config.tw_streamer, self.recording_folder) - self.chat_recorder = TwitchChatRecorder(self.api, config.tw_streamer, self.recording_folder) + self.streamer_folder = os.path.join(self.config.output_folder, self.config.tw_streamer) + self.video_recorder = TwitchVideoRecorder() + self.chat_recorder = TwitchChatRecorder(self.api, debug=True) def run(self): - if os.path.isdir(self.recording_folder) is False: - logger.info("Recording folder `%s` does not exists. Create it", self.recording_folder) - os.makedirs(self.recording_folder) - while True: logger.info("Start watching streamer %s", self.config.tw_streamer) status = self.api.get_user_status(self.config.tw_streamer) if status == TwitchStreamStatus.ONLINE: logger.info("Streamer %s is online. Start recording", self.config.tw_streamer) - now = datetime.now() - file_template = "{0}-{1}".format(self.config.tw_streamer, now.strftime("%H-%M-%S")) - self.chat_recorder.run(file_template) + start_time = datetime.now() + record_folder_name = start_time.strftime("%d-%m-%Y_%H-%M-%S") + record_folder = os.path.join(self.streamer_folder, record_folder_name) + os.makedirs(record_folder) + + chat_file = os.path.join(record_folder, "chat.txt") + video_file = os.path.join(record_folder, "video.mp4") + + self.chat_recorder.run(self.config.tw_streamer, chat_file) # self.video_recorder.run(file_template) logger.info("Streamer %s has finished stream", self.config.tw_streamer) diff --git a/clipper/video.py b/clipper/video.py index a3045bd..5b3a4fb 100644 --- a/clipper/video.py +++ b/clipper/video.py @@ -1,51 +1,30 @@ -import datetime import logging -import os import subprocess logger = logging.getLogger(__name__) class TwitchVideoRecorder: - access_token = None + is_running = False + refresh_timeout = 15 + streamlink_process = None - def __init__(self, authenticator, streamer_name, recording_folder, quality="480p", on_finish=None): - # global configuration - self.disable_ffmpeg = False - self.refresh_timeout = 15 - self.recording_folder = recording_folder - self.stream_uid = None - self.on_finish = on_finish + def run(self, streamer_name, output_file, quality="480p"): + self._record_stream(streamer_name, output_file, quality) - # twitch configuration - self.streamer_name = streamer_name - self.quality = quality - self.authenticator = authenticator + def stop(self): + if self.streamlink_process: + self.streamlink_process.kill() - def run(self): - # make sure the interval to check user availability is not less than 15 seconds - if self.refresh_timeout < 15: - logger.warning("check interval should not be lower than 15 seconds") - self.refresh_timeout = 15 - logger.warning("check interval set to 15 seconds") - - self.record_stream(self.streamer_name, recording_path) - - def record_stream(self, channel, recording_path): - filename = self.streamer_name + " - " + datetime.datetime.now() \ - .strftime("%Y-%m-%d %Hh%Mm%Ss") + " - " + channel.get("title") + ".mp4" - - filename = "".join(x for x in filename if x.isalnum() or x in [" ", "-", "_", "."]) - recorded_filename = os.path.join(recording_path, filename) - - # start streamlink process - subprocess.call([ + def _record_stream(self, streamer_name, output_file, quality): + # subprocess.call() + self.streamlink_process = subprocess.Popen([ "streamlink", "--twitch-disable-ads", - "twitch.tv/" + self.streamer_name, - self.quality, + "twitch.tv/" + streamer_name, + quality, "-o", - recorded_filename + output_file ]) - return recorded_filename + self.is_running = True