Seems finished

This commit is contained in:
Vitalii Lebedynskyi
2022-08-17 16:51:05 +03:00
parent 173fcc098f
commit fdfd2c6135
3 changed files with 121 additions and 23 deletions

View File

@@ -5,17 +5,18 @@ import matplotlib.pyplot as plt
from datetime import datetime
from clipper.chat import CHAT_DIVIDER
logger = logging.getLogger(__name__)
class ChatAnalyser:
def __init__(self, ignore_commands=True, ignored_users=["moobot", "nightbot"]):
def __init__(self, ignore_commands=True, ignored_users=None):
if ignored_users is None:
ignored_users = ["moobot", "nightbot"]
self.ignored_users = ignored_users
self.ignore_commands = ignore_commands
def run(self, chat_file, peaks_output_file, peaks_output_chart):
def run(self, chat_file, peaks_output_file, peaks_output_chart, start_time):
dates = self._read_message_dates(chat_file)
messages_per_minute = self._group_dates(dates)
peaks = self._find_peeks(messages_per_minute, peaks_output_file, peaks_output_chart)
@@ -31,19 +32,23 @@ class ChatAnalyser:
if not line:
break
message_data = line.split(CHAT_DIVIDER)
message_data = line.split("<~|~>")
if len(message_data) != 3:
# Wrong line format
continue
if message_data[1].lower() in self.ignore_commands:
if message_data[1].lower() in self.ignored_users:
continue
if self.ignore_commands and message_data[2].startswith("!"):
continue
date = message_data[0]
dates.append(self._parse_date(date))
try:
dates.append(self._parse_date(date))
except BaseException as e:
logger.error(e)
return dates
def _parse_date(self, date_str):
@@ -64,20 +69,39 @@ class ChatAnalyser:
def _find_peeks(self, messages_per_minute, peaks_output_file, peaks_output_chart):
y_coordinates = list(messages_per_minute.values())
x_coordinates = list(messages_per_minute.keys())
peak_indices = scipy.signal.find_peaks_cwt(np.array(y_coordinates), 1)
peak_indices = scipy.signal.find_peaks_cwt(np.array(y_coordinates), 0.5)
x_hours = [x.split(" ")[1] for x in x_coordinates]
fig, ax = plt.subplots()
ax.plot(x_hours, y_coordinates)
fig.autofmt_xdate()
plt.xlabel("Time")
plt.ylabel("Count")
ax.plot(range(0, len(y_coordinates), 1), y_coordinates)
plt.xlabel("Video Minutes")
plt.ylabel("Message count")
plt.title("Stream chat reaction")
plt.savefig(peaks_output_chart)
peak_values = [x_coordinates[index] for index in peak_indices]
start_time = None
if len(x_coordinates) > 0:
start_time = datetime.strptime(x_coordinates[0], "%Y-%m-%d %H:%M")
max_value = max(y_coordinates)
trash_hold_value = max_value * 0.75
filtered_values = [x_coordinates[index] for index in peak_indices if y_coordinates[index] > trash_hold_value]
with open(peaks_output_file, "w") as stream:
for peak in peak_values:
stream.writelines(f"{peak}\n")
for peak in filtered_values:
if start_time:
peak_time = datetime.strptime(peak, "%Y-%m-%d %H:%M")
diff = peak_time - start_time
minutes = divmod(diff.total_seconds() / 60, 60)
stream.writelines(f"{peak} -> {minutes}\n")
else:
stream.writelines(f"{peak}\n")
return peak_indices
if __name__ == "__main__":
anal = ChatAnalyser()
chat_file = "/Users/vetalll/Projects/Python/TwitchClipper/recorded/vovapain/17-08-2022_08-33-23/chat.txt"
out_file = "/Users/vetalll/Projects/Python/TwitchClipper/recorded/vovapain/17-08-2022_08-33-23/chat_peaks.txt"
out_hraph = "/Users/vetalll/Projects/Python/TwitchClipper/recorded/vovapain/17-08-2022_08-33-23/chat_chart.png"
anal.run(chat_file, out_file, out_hraph, datetime(2022, 8, 15, 20, 38, 49))

View File

@@ -1,3 +1,72 @@
import logging
import os
import subprocess
from datetime import datetime
from datetime import timedelta
logger = logging.getLogger(__name__)
class Clipper:
def run(self):
pass
def run(self, video_file, chat_peaks_file, output_folder):
try:
self._run(video_file, chat_peaks_file, output_folder)
except BaseException as e:
logger.error(e)
def _run(self, video_file, chat_peaks_file, output_folder):
if not os.path.isdir(output_folder):
os.mkdir(output_folder)
with open(chat_peaks_file, "r") as stream:
lines = stream.readlines()
if not lines:
logger.error("No peaks found")
return
counter = 1
for line in lines:
# l = "2022-08-17 10:15 -> (1.0, 42.0)"
time_part = line.split("->")[1].strip() # (1.0, 42.0)
time = time_part.replace("(", "").replace(")", "").split(",")
video_time = datetime(2000, 1, 1, int(float(time[0])), int(float(time[1])), 0, 0)
start_time = video_time - timedelta(minutes=1)
end_time = video_time + timedelta(minutes=1)
ffmpeg_start_time = start_time.strftime("%H:%M:00")
ffmpeg_end_time = end_time.strftime("%H:%M:00")
output_file = os.path.join(output_folder, f"clip_{counter}.mp4")
self._cut_clip(video_file, ffmpeg_start_time, ffmpeg_end_time, output_file)
counter = counter + 1
def _cut_clip(self, video_file, start_time, end_time, output_name):
# ffmpeg -ss 00:01:00 -to 00:02:00 -i input.mp4 -c copy output.mp4
try:
subprocess.call([
"ffmpeg",
"-err_detect",
"ignore_err",
"-ss",
start_time,
"-to",
end_time,
"-i",
video_file,
"-c",
"copy",
output_name
])
except BaseException as e:
logger.error("Unable to run streamlink")
logger.error(e)
if __name__ == "__main__":
"/Users/vetalll/Projects/Python/TwitchClipper/recorded/"
video = "/Users/vetalll/Projects/Python/TwitchClipper/recorded/icebergdoto/17-08-2022_13-30-20/video.mp4"
peaks = "/Users/vetalll/Projects/Python/TwitchClipper/recorded/icebergdoto/17-08-2022_13-30-20/chat_peaks.txt"
result = "/Users/vetalll/Projects/Python/TwitchClipper/recorded/icebergdoto/17-08-2022_13-30-20/clips"
clipper = Clipper()
clipper.run(video, peaks, result)

View File

@@ -7,6 +7,7 @@ from datetime import datetime
from clipper.analyser import ChatAnalyser
from clipper.api import TwitchApi, TwitchStreamStatus
from clipper.chat import TwitchChatRecorder
from clipper.clipper import Clipper
from clipper.video import TwitchVideoRecorder
logger = logging.getLogger(__name__)
@@ -29,6 +30,7 @@ class Recorder:
self.video_recorder = TwitchVideoRecorder()
self.chat_recorder = TwitchChatRecorder(self.api, debug=True)
self.chat_analyser = ChatAnalyser()
self.clipper = Clipper()
def run(self):
logger.info("Start recording streamer %s", self.config.tw_streamer)
@@ -45,13 +47,11 @@ class Recorder:
output_video_file = os.path.join(record_folder, "video.mp4")
output_chat_file = os.path.join(record_folder, "chat.txt")
output_chat_peaks_file = os.path.join(record_folder, "chat_peaks.txt")
output_chat_chart_file = os.path.join(record_folder, "chat_chart.png")
self.chat_recorder.run(self.config.tw_streamer, output_chat_file)
self.video_recorder.run(self.config.tw_streamer, output_video_file, quality="160p")
self._loop_recording()
self._post_process_video(output_chat_file, output_chat_peaks_file, output_chat_chart_file)
self._post_process_video(record_folder, output_chat_file, output_video_file, start_time)
elif status == TwitchStreamStatus.OFFLINE:
logger.info("Streamer %s is offline. Waiting for 300 sec", self.config.tw_streamer)
@@ -77,7 +77,12 @@ class Recorder:
continue
break
def _post_process_video(self, output_chat_file, output_chat_peaks_file, output_chat_chart_file):
def _post_process_video(self, record_folder, output_chat_file, output_video_file, start_time):
output_chat_peaks_file = os.path.join(record_folder, "chat_peaks.txt")
output_chat_chart_file = os.path.join(record_folder, "chat_chart.png")
logger.info("Start looking for peaks in file %s", output_chat_file)
peaks = self.chat_analyser.run(output_chat_file, output_chat_peaks_file, output_chat_chart_file)
peaks = self.chat_analyser.run(output_chat_file, output_chat_peaks_file, output_chat_chart_file, start_time)
logger.info("Found peaks: %s for file %s", len(peaks), output_chat_file)
self.clipper.run(output_video_file, output_chat_peaks_file, record_folder)