def build_ref(): subs = SSAFile() subs.info["My Custom Info"] = "Some: Test, String." subs.styles["left"] = SSAStyle(alignment=7, bold=True) subs.styles["topleft"] = SSAStyle(alignment=4) subs.append(SSAEvent(start=0, end=make_time(m=1), text="An, example, subtitle.")) subs.append(SSAEvent(start=0, end=make_time(m=1), type="Comment", text="You can't see this one.")) subs.append(SSAEvent(start=make_time(m=1), end=make_time(m=2), text="Subtitle number\\Ntwo.")) return subs
def resolve_inner_timestamps(self): ret = copy.deepcopy(self.inner) if isinstance(self.inner, srt.Subtitle): ret.start = self.start ret.end = self.end elif isinstance(self.inner, pysubs2.SSAEvent): ret.start = pysubs2.make_time(s=self.start.total_seconds()) ret.end = pysubs2.make_time(s=self.end.total_seconds()) else: raise NotImplementedError('unsupported subtitle type: %s' % type(self.inner)) return ret
def test_simple_read(): text = dedent("""\ 00:00:00:ten--chars 00:01:00:ten--chars-ten-chars """) #calculate endtime from starttime + 500 miliseconds + 67 miliseconds per each character (15 chars per second) ref = SSAFile() ref.append(SSAEvent(start=0, end=make_time(ms=1840), text="ten--chars")) ref.append(SSAEvent(start=make_time(m=1), end=make_time(ms=62510), text="ten--chars-ten-chars")) subs = SSAFile.from_string(text) assert subs.equals(ref)
def test_overlapping_read(): # see issue #35 text = dedent("""\ 00:00:12:I ... this is some long text ... ... this is some long text ... 00:00:14:observing ... ... this is some long text ... ... this is some long text ... 00:00:18:and ... ... this is some long text ... ... this is some long text ... 00:00:22:You ... ... this is some long text ... ... this is some long text ... """) subs = SSAFile.from_string(text) assert subs[0].start == make_time(s=12) assert subs[0].end == subs[1].start == make_time(s=14) assert subs[1].end == subs[2].start == make_time(s=18) assert subs[2].end == subs[3].start == make_time(s=22)
def test_keep_unknown_html_tags(): # see issue #26 text = dedent("""\ 1 00:00:10,500 --> 00:00:13,000 <i>Elephant's <sub>Little</sub> Dream</i> 2 00:00:15,000 --> 00:00:18,000 <font color="cyan">At the left we can see...</font> """) ref_default = SSAFile() ref_default.append(SSAEvent(start=make_time(s=10.5), end=make_time(s=13), text="{\\i1}Elephant's Little Dream{\\i0}")) ref_default.append(SSAEvent(start=make_time(s=15), end=make_time(s=18), text="At the left we can see...")) ref_keep = SSAFile() ref_keep.append(SSAEvent(start=make_time(s=10.5), end=make_time(s=13), text="{\\i1}Elephant's <sub>Little</sub> Dream{\\i0}")) ref_keep.append(SSAEvent(start=make_time(s=15), end=make_time(s=18), text="<font color=\"cyan\">At the left we can see...</font>")) subs_default = SSAFile.from_string(text) subs_keep = SSAFile.from_string(text, keep_unknown_html_tags=True) assert subs_default.equals(ref_default) assert subs_keep.equals(ref_keep) assert subs_keep.to_string("srt") == ref_keep.to_string("srt")
def test_read_bad_tags(): """missing opening/closing tag, bad nesting, extra whitespace""" text = dedent("""\ 1 00:00:10,500 --> 00:00:13,000 < u><i><font color="red" >Elephant's < s>Dream< / i > Is Long</s> And Badly Nested</xyz> """) ref = SSAFile() ref.append(SSAEvent(start=make_time(s=10.5), end=make_time(s=13), text="{\\u1}{\\i1}Elephant's {\\s1}Dream{\\i0} Is Long{\\s0} And Badly Nested")) subs = SSAFile.from_string(text) assert subs.equals(ref)
def convert_yt_comments(jsonname, comment_duration, video_info, outputname): with open(jsonname) as f: yt_comments = json.load(f) if len(yt_comments) == 0: return subs = pysubs2.SSAFile() subs.info["PlayResX"] = 384 subs.info["PlayResY"] = 288 start_time_shift = yt_comments[0]["time_in_seconds"] * 1000 comment_channel = [] comment_size = 20 for i in range(0, subs.info["PlayResY"], comment_size): comment_channel.append(None) for msg in yt_comments: now = msg["time_in_seconds"] * 1000 if now > video_info["duration"] * 1000: # print(now, ">", video_info["duration"] * 1000) continue if not msg["message"]: continue selected_channel = 1 for index, chan in enumerate(comment_channel): if (not chan or chan["time_in_seconds"] * 1000 + (200 * len(msg["message"])) < now): comment_channel[index] = msg selected_channel = index + 1 break movement = ("{\move(414," + str(selected_channel * 20) + ",-30," + str(selected_channel * 20) + ",0," + str(comment_duration) + ")}") subs.append( pysubs2.SSAEvent( start=pysubs2.make_time(ms=msg["time_in_seconds"] * 1000), end=pysubs2.make_time(ms=(msg["time_in_seconds"] * 1000) + comment_duration), text=movement + msg["message"])) subs.shift(ms=-start_time_shift + 100) subs.save(outputname)
def test_read_malformed(): """no line number, no empty line, leading whitespace, bad timestamp format""" text = dedent("""\ 00:00:00.000 ->00:01:00.000 An example subtitle. 0:01:00,00 --> 0:02:00,00 Subtitle number two. """) ref = SSAFile() ref.append(SSAEvent(start=0, end=make_time(m=1), text="An example subtitle.")) ref.append(SSAEvent(start=make_time(m=1), end=make_time(m=2), text="Subtitle number\\Ntwo.")) subs = SSAFile.from_string(text) assert subs.equals(ref)
def save_to_subtitles(results, formatter): """ Save to subtitle file :param results: Dictionary containing info and start/end times :param formatter: Apply text formating to the subtitle :return: New subtitle file """ subs = SSAFile() for result in results: event = SSAEvent(start=make_time(s=result['start']), end=make_time(s=result['end']), text=formatter(result)) if 'highlight' in result and result['highlight']: event.style = 'red' subs.append(event) logger.info(f'Processed {len(results)} results to subtitle events') return subs
def build_ref(): subs = SSAFile() subs.info["My Custom Info"] = "Some: Test, String." subs.styles["left"] = SSAStyle(alignment=7, bold=True) subs.styles["topleft"] = SSAStyle(alignment=4) subs.append( SSAEvent(start=0, end=make_time(m=1), text="An, example, subtitle.")) subs.append( SSAEvent(start=0, end=make_time(m=1), type="Comment", text="You can't see this one.")) subs.append( SSAEvent(start=make_time(m=1), end=make_time(m=2), text="Subtitle number\\Ntwo.")) return subs
def test_negative_timestamp_read(): ref = build_ref() subs = SSAFile.from_string(NEGATIVE_TIMESTAMP_ASS_REF) # negative timestamp is read correctly assert subs[0].start == -make_time(1, 59, 54, 930) # negative times are flushed to zero on output assert ref.to_string("ass") == subs.to_string("ass")
def test_simple_read(): text = dedent("""\ 1 00:00:00,000 --> 00:01:00,000 An example subtitle. 2 00:01:00,000 --> 00:02:00,000 Subtitle number two. """) ref = SSAFile() ref.append(SSAEvent(start=0, end=make_time(m=1), text="An example subtitle.")) ref.append(SSAEvent(start=make_time(m=1), end=make_time(m=2), text="Subtitle number\\Ntwo.")) subs = SSAFile.from_string(text) assert subs.equals(ref)
def test_read_position_styling(): """position is ignored, italic is converted, color is ignored""" text = dedent("""\ 1 00:00:10,500 --> 00:00:13,000 X1:63 X2:223 Y1:43 Y2:58 <i>Elephant's Dream</i> 2 00:00:15,000 --> 00:00:18,000 X1:53 X2:303 Y1:438 Y2:453 <font color="cyan">At the left we can see...</font> """) ref = SSAFile() ref.append(SSAEvent(start=make_time(s=10.5), end=make_time(s=13), text="{\\i1}Elephant's Dream{\\i0}")) ref.append(SSAEvent(start=make_time(s=15), end=make_time(s=18), text="At the left we can see...")) subs = SSAFile.from_string(text) assert subs.equals(ref)
async def lyrics(self, ctx): await self.bot.safe_delete(ctx.message) if not (self.bot.videoPlaying): # Song isnt playing await ctx.send(embed=(await self.bot.generate_error(self, "There are no songs playing!")), delete_after=20) return if not (self.bot.subtitlesexist): # Subtiles do not exist await ctx.send(embed=(await self.bot.generate_error(self, "There are no saved Lyrics for this song.")), delete_after=20) return counter = 0; firstCycle = True; msg = None pysubs2.make_time(s=1) allsubtitles = self.bot.videoData["captions"] letterCounter = 0 letterBlock = 0 letterBlocks = {} currentBlock = [] currentBlock.append("Lyrics for '" + self.bot.videoData["title"] + "' as requested by '" + (self.bot.videoData["requester"]).name + "'.\n(These Lyrics are straight from the YouTube video provided, so any issues are the fault of the uploader, Not us)\n\n") for line in allsubtitles: letterCounter += len(line) currentBlock.append(line) if letterCounter > 500: letterBlocks[str(letterBlock)] = currentBlock letterBlock += 1 currentBlock = [] letterCounter = 0 if not (len(currentBlock) == 0): letterBlocks[str(letterBlock)] = currentBlock letterBlock += 1 for i in range(letterBlock): block = letterBlocks[str(i)] string = ("\n".join(block)) await ctx.send("```\n" + string + "\n```") return
def make_ass(wav, segments, transcriptions, utt2spk, ass): """ Формирование .ASS файла из транскрибаций Аргументы: wav: наименование аудио файла segments: путь к файлу описания сегментов transcriptions: путь к файлу транскрибации utt2spk: путь к файлу сопоставления сегментов и говорящих ass: путь к .ASS файлу субтитров """ sub = pysubs2.SSAFile() sub.info['Title'] = 'Default Aegisub file' sub.info['YCbCr Matrix'] = 'None' sub.aegisub_project['Audio File'] = wav sub.aegisub_project['Scroll Position'] = 0 sub.aegisub_project['Active Line'] = 0 segments_df = pd.read_csv(segments, header=None, sep=' ', names=['utt_id', 'wav', 'start', 'end']) transcriptions_df = pd.read_csv(transcriptions, sep='\t', header=None, names=['utt_id', 'text']) utt2spk_df = pd.read_csv(utt2spk, sep='\t', header=None, names=['utt_id', 'speaker']) events = segments_df.merge(transcriptions_df, how='left', on='utt_id').merge(utt2spk_df, how='left', on='utt_id').fillna('') for row in events.values: event = pysubs2.SSAEvent(start=pysubs2.make_time(s=float(row[2])), end=pysubs2.make_time(s=float(row[3])), text=row[4], name=row[5]) sub.events.append(event) sub.sort() sub.save(ass, format_='ass')
def test_read_complex(): # regression test for #30 text = dedent("""\ WEBVTT X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:0 00:50.099 --> 00:53.299 line:85% align:middle Cuidem do seu grupo. Cuidem de suas fileiras. 01:54.255 --> 01:55.455 line:85% align:middle Parem! 01:58.155 --> 01:59.555 line:85% align:middle E, parem! """) ref = SSAFile() ref.append( SSAEvent(start=make_time(s=50, ms=99), end=make_time(s=53, ms=299), text=r"Cuidem do seu grupo.\NCuidem de suas fileiras.")) ref.append( SSAEvent(start=make_time(m=1, s=54, ms=255), end=make_time(m=1, s=55, ms=455), text="Parem!")) ref.append( SSAEvent(start=make_time(m=1, s=58, ms=155), end=make_time(m=1, s=59, ms=555), text="E, parem!")) subs = SSAFile.from_string(text) assert subs.equals(ref)
def test_empty_subtitles(): # regression test for issue #11 text = dedent(""" 392 00:29:27,46 --> 00:29:29,83 I'm Liza Minnelli.. 393 00:00:00,00 --> 00:00:00,00 394 00:00:00,00 --> 00:00:00,00 """) ref = SSAFile() ref.append(SSAEvent(start=make_time(m=29, s=27, ms=460), end=make_time(m=29, s=29, ms=830), text="I'm Liza Minnelli..")) ref.append(SSAEvent(start=0, end=0, text="")) ref.append(SSAEvent(start=0, end=0, text="")) subs = SSAFile.from_string(text) assert subs.equals(ref)
def test_repr_simple(): subs = SSAFile() subs.append(SSAEvent(start=make_time(m=5), end=make_time(m=6))) subs.append(SSAEvent(start=make_time(m=125), end=make_time(m=126))) subs.append(SSAEvent(start=make_time(m=15), end=make_time(m=16))) subs.styles["style1"] = SSAStyle() subs.styles["style2"] = SSAStyle() ref = "<SSAFile with 3 events and 3 styles, last timestamp 2:06:00>" assert repr(subs) == ref
def test_simple_parsing(): test_input1 = "[123][456] Line 1" subs1 = SSAFile.from_string(test_input1) assert len(subs1) == 1 assert subs1[0] == SSAEvent(start=make_time(ms=12300), end=make_time(ms=45600), text="Line 1") test_input2 = "[123][456] / Line 1| Line 2/2" subs2 = SSAFile.from_string(test_input2) assert len(subs2) == 1 assert subs2[0] == SSAEvent(start=make_time(ms=12300), end=make_time(ms=45600), text="{\i1}Line 1{\i0}\\NLine2/2") test_input3 = dedent(""" [123][456] Line 1 [321][456] / Line 2| Line 3 (123)(456)This line should not be parsed This line should also not be parsed [789][1234] /Line 4""") subs3 = SSAFile.from_string(test_input3) assert len(subs3) == 3 assert subs3[0] == SSAEvent(start=make_time(ms=12300), end=make_time(ms=45600), text="Line 1") assert subs3[1] == SSAEvent(start=make_time(ms=32100), end=make_time(ms=45600), text="{\i1}Line 2{\i0}\\NLine 3") assert subs3[2] == SSAEvent(start=make_time(ms=78900), end=make_time(ms=123400), text="{\i1}Line 4{\i0}")
sub.text.find('下载24小时') != -1) or (sub.text.find('.com') != -1): subtitle_ass.remove(sub) temp = copy.deepcopy(subtitle_ass) temp.clear() for sub in list(subtitle_ass): f = True for sub2 in list(temp): if sub.text == sub2.text: f = False break if f: temp.append(sub) flag_text = "<<" + str(i) + ">>组字幕" temp.insert(0, SSAEvent(start=make_time(s=0), end=make_time(s=0), text=flag_text)) temp.save(work_dir + '.srt') # subtitle_ass = SSAFile.load(work_dir+'.srt') # for sub in list(subtitle_ass): # if sub.text == '' or (sub.text.find('字幕组') != -1) or (sub.text.find('24小') != -1) or (sub.text.find('字幕組') != -1) or ( # sub.text.find('下載24小時') != -1) or (sub.text.find('翻译:') != -1) or (sub.text.find('下载24小时') != -1): # subtitle_ass.remove(sub) # text = sub.text # subtitle_ass = SSAFile.load(work_dir+'\[AngelSub][Another][01][BD][BIG5][X264].ass') # text = subtitle_ass[306] # text = subtitle_ass[28] # bool = text.text.find('下載24小時') # b = (text.text.find('下载24小时') != -1)
def renderVideo(video, rendering_screen): global render_current_progress, render_max_progress, render_message t0 = datetime.datetime.now() clips = video.clips colour1 = getColour(video.colour1) colour2 = getColour(video.colour2) music_type = video.audio_cat subprocess._cleanup = lambda: None credits = [] streamers_in_cred = [] render_current_progress = 0 # see where render_current_progress += 1 amount = 0 for clip in clips: if clip.isUsed: amount += 1 render_max_progress = amount * 3 + 1 + 1 render_message = "Beginning Rendering" rendering_screen.render_progress.emit() current_date = datetime.datetime.today().strftime("%m-%d-%Y__%H-%M-%S") fClips = [] start_duration = 0 end_duration = 0 # render progress 1 for i, clip in enumerate(clips): if clip.isUsed: name = clip.streamer_name mp4 = clip.mp4 subs = SSAFile() if name is not None and name not in streamers_in_cred and not clip.isUpload: credits.append( f"Streamer: {clip.streamer_name} Channel: {clip.channel_url}" ) streamers_in_cred.append(clip.streamer_name) if clip.start_cut is None: clip.start_cut = 0 if clip.end_cut is None: clip.end_cut = 0 start_trim = round(clip.start_cut / 1000, 1) end_trim = round(clip.end_cut / 1000, 1) final_duration = round(clip.vid_duration - end_trim - start_trim, 1) audio = clip.audio if name is None: name = "" render_message = f"Adding text ({i + 1}/{len(clips)})" rendering_screen.render_progress.emit() subs.styles['vidText'] = SSAStyle(alignment=7, fontname='Gilroy-ExtraBold', fontsize=25, marginl=4, marginv=-2.5, marginr=0, outline=2, outlinecolor=colour2, primarycolor=colour1, shadow=0) if settings.includeStreamerName: subs.append( SSAEvent(start=make_time(s=0), end=make_time(s=60), style='vidText', text=f"{name}")) subs.save(f'subtitleFile.ass') render_current_progress += 1 render_message = f"Done Adding text ({i + 1}/{len(clips)})" rendering_screen.render_progress.emit() print(f"Adding text ({i + 1}/{len(clips)}) to video: {mp4}.mp4") render_message = f"Adding text to video ({i + 1}/{len(clips)})" rendering_screen.render_progress.emit() print( f"Rendering video ({i + 1}/{len(clips)}) to \"{settings.vid_finishedvids}\"/{mp4}_finished.mp4" ) mp4name = mp4 mp4path = f"{mp4}.mp4" if len(mp4.split("/")) > 2: name = len(mp4.split("/")) mp4name = mp4.split("/")[name - 1].replace(".mp4", "") mp4path = mp4[1:] if not clip.isInterval and not clip.isIntro: print("%s duration %s" % (mp4name, final_duration)) if end_trim == 0 and start_trim == 0: print("%s no trim" % mp4name) os.system( f"ffmpeg -y -fflags genpts -i \"{mp4path}\" -vf \"ass=subtitleFile.ass, scale=1920:1080\" \"{settings.vid_finishedvids}/{mp4name}_finished.mp4\"" ) elif end_trim > 0 and start_trim > 0: print("%s start trim %s and end trim %s" % (mp4name, start_trim, end_trim)) os.system( f"ffmpeg -y -fflags genpts -i \"{mp4path}\" -ss {start_trim} -t {final_duration} -vf \"ass=subtitleFile.ass, scale=1920:1080\" \"{settings.vid_finishedvids}/{mp4name}_finished.mp4\"" ) elif end_trim > 0 and start_trim == 0: print("%s end trim %s" % (mp4name, end_trim)) os.system( f"ffmpeg -y -fflags genpts -i \"{mp4path}\" -t {clip.vid_duration - end_trim} -vf \"ass=subtitleFile.ass, scale=1920:1080\" \"{settings.vid_finishedvids}/{mp4name}_finished.mp4\"" ) elif end_trim == 0 and start_trim > 0: print("%s start trim %s" % (mp4name, start_trim)) os.system( f"ffmpeg -y -fflags genpts -i \"{mp4path}\" -ss {start_trim} -vf \"ass=subtitleFile.ass, scale=1920:1080\" \"{settings.vid_finishedvids}/{mp4name}_finished.mp4\"" ) render_current_progress += 1 render_message = f"Done Adding text to video ({i + 1}/{len(clips)})" rendering_screen.render_progress.emit() render_message = f"Adding clip to list ({i + 1}/{len(clips)})" rendering_screen.render_progress.emit() if not clip.isInterval and not clip.isIntro: finish = VideoFileClip( f'{settings.vid_finishedvids}/{mp4name}_finished.mp4').fx( afx.volumex, audio) else: finish = VideoFileClip(f'{mp4path}').fx(afx.volumex, audio) end_duration += finish.duration fClips.append(finish) render_current_progress += 1 render_message = f"Done Adding clip to list ({i + 1}/{len(clips)})" rendering_screen.render_progress.emit() if i <= 2: start_duration += finish.duration musicFiles = getFileNames(f'{settings.asset_file_path}/Music') random.shuffle(musicFiles) print("done working out durations") final_concat = concatenate_videoclips(fClips) print("done combining clips") print(musicFiles) # render progress 2 render_message = "Creating audio loop" rendering_screen.render_progress.emit() #audio = AudioFileClip(f'{settings.asset_file_path}/Music/{musicFiles[0]}.mp3').fx(afx.volumex, float(video.background_volume)) print("Using music type %s" % music_type) musicFolders = (getFileNames("Assets/Music")) if not musicFolders: music_type = "None" print("No music folders, defaulting to no audio") if not music_type == "None": to_combine = [] music_combined_duration = 0 while music_combined_duration < end_duration: random_file = random.choice( os.listdir(f'{settings.asset_file_path}/Music/{music_type}')) sound1 = AudioSegment.from_wav( f"Assets/Music/{music_type}/{random_file}") music_combined_duration += sound1.duration_seconds to_combine.append(sound1) combined_sounds = sum(to_combine) combined_sounds.export(f"{settings.temp_path}/music-loop-uncut.wav", format="wav") audio_loop_without_pause = AudioSegment.from_wav( "%s/music-loop-uncut.wav" % settings.temp_path) new_audio = AudioSegment.silent( duration=(start_duration * 1000)) + audio_loop_without_pause new_audio.export( f"{settings.temp_path}/music-loop-uncut_with_pause.mp3", format="mp3") music_loop = afx.audio_loop(AudioFileClip( f"{settings.temp_path}/music-loop-uncut_with_pause.mp3"), duration=end_duration).fx( afx.volumex, float(video.background_volume)) #music_loop = afx.audio_loop(audio, duration=end_duration) music_loop.write_audiofile(f'{settings.temp_path}/music-loop.mp3') render_current_progress += 1 render_message = "Done Creating audio loop" rendering_screen.render_progress.emit() # render progress 3 render_message = "Writing final video" rendering_screen.render_progress.emit() if not music_type == "None": final_vid_with_music = final_concat.set_audio( CompositeAudioClip([ final_concat.audio, AudioFileClip(f'{settings.temp_path}/music-loop.mp3') ])) else: final_vid_with_music = final_concat.set_audio(final_concat.audio) sleep(5) final_vid_with_music.write_videofile( f'{settings.final_video_path}/TwitchMoments_{current_date}.mp4', fps=settings.fps, threads=16) render_current_progress += 1 t1 = datetime.datetime.now() total = t1 - t0 render_message = "Done writing final video (%s)" % total rendering_screen.render_progress.emit() f = open(f"{settings.final_video_path}/TwitchMoments_{current_date}.txt", "w+") f.write("A special thanks to the following: \n\n") for cred in credits: f.write(cred + "\n") f.close() sleep(10)
def download(request): # must be a post method if request.method != "POST": Http404 # Get data from post data = json.loads(request.POST.get('data')) # Get data format dataFormat = data["format"].lower() # for Mkv subformat must be srt if dataFormat != "mkv": subFormat = dataFormat else: subFormat = "srt" # Se if we want to include the data includeData = data["includeData"] # Get labels, convert and sort them ev = data["events"] events = [{ "ts": float(value["ts"]), "label": value["label"] if "label" in value else "" } for _, value in ev.items()] events = sorted(events, key=lambda k: k['ts']) sessionID = request.session.session_key sessionInfo = request.session.get('dataInfo', {"type": "unknown"}) WORKING_FOLDER = os.path.join(MEDIA_ROOT, sessionID) if not os.path.exists(WORKING_FOLDER): os.makedirs(WORKING_FOLDER, exist_ok=True) # Files used for creation, that need to be removed at the end tmpFiles = [] # First lets remove all old files in the directory for a in os.listdir(WORKING_FOLDER): print(a) f = os.path.join(WORKING_FOLDER, a) if "filePath" in sessionInfo and f == sessionInfo["filePath"]: continue subprocess.check_output(["rm", "-rf", f]) wsManager.sendStatus(sessionID, "Gathering data...", percent=10) # We need to create mkv first if sessionInfo["type"] in ["fired", "uploaded"]: filePath = sessionInfo["filePath"] info = mkv.info(filePath)["streams"] # Just the info for 1st audio in file dataDict = next(s for s in info if s["type"] == "audio") streamIndex = dataDict["streamIndex"] else: fn = sessionInfo["filePath"] filePath = os.path.join(WORKING_FOLDER, fn) dataDict = dataHp.getSessionData(sessionID, sessionInfo) if dataDict is None: wsManager.sendStatus( sessionID, "Error getting {} data!".format(sessionInfo["type"].upper())) return if includeData: wsManager.sendStatus(sessionID, "Creating MKV from {} data...".format( sessionInfo["type"].upper()), percent=20) # Should be deleted afterwards tmpFiles.append(filePath) mkv.makeMkv([dataDict], filePath, verbose=DEBUG_FFMPEG) streamIndex = 0 origFileName = filePath ts = 0 if "timestamp" in dataDict: ts = dataDict["timestamp"] duration = dataDict["duration"] # Maybe we have to cut the data here as we want only a range selRange = None if "range" in data: selRange = data["range"] print("Range:" + timeStr(selRange[0]) + "->" + timeStr(selRange[1])) start = selRange[0] - ts stop = selRange[1] - ts duration = stop - start ts = selRange[0] # Cut the data for range if includeData: wsManager.sendStatus(sessionID, "Cutting data to selected range...", percent=50) tmpRangeData = os.path.join(WORKING_FOLDER, "range_" + os.path.basename(filePath)) # Force reencode s.t. timestamps match. copy somehow causes problems systemCall = "ffmpeg -hide_banner -i {} -map 0 -c:a wavpack -map_metadata 0 -metadata:s:a:0 timestamp=\"{}\" -ss {:.3f} -t {:.3f} -y {}".format( filePath, selRange[0], float(start), round(duration, 3), tmpRangeData) mkv.__call_ffmpeg(systemCall, verbose=DEBUG_FFMPEG) tmpFiles.append(tmpRangeData) # This is with data loading and making mkv but its f*****g slow # dataList = next(mkv.chunkLoad(filePath, duration, starttime=start, stoptime=stop))[0] # if "TIMESTAMP" in dataList["metadata"]: dataList["metadata"]["TIMESTAMP"] = dataList["timestamp"] # print(selRange[0]) # print(dataList["timestamp"]) # tmpRangeData = os.path.join(MEDIA_ROOT, "tmp_data.mkv") # mkv.makeMkv([dataList], tmpRangeData, verbose=DEBUG_FFMPEG) filePath = tmpRangeData wsManager.sendStatus(sessionID, "Creating subtitles from labels..", percent=75) # Convert events to pysubs2 subs = SSAFile() # If we have only one state for i in range(len(events)): start = events[i]["ts"] / 1000 - ts if i + 1 >= len(events): end = duration else: end = events[i + 1]["ts"] / 1000 - ts # If range was selected remove events outside bounds and cap the events to boarder if start < 0 and end < 0: continue if start > duration: continue # Cap start = max(start, 0) end = min(end, duration) # print(timeStr(ts+events[i]["ts"]/1000) + "->" + events[i]["label"]) label = events[i]["label"] if label == "" or label.isspace(): label = "unknown" ev = SSAEvent(start=make_time(s=start), end=make_time(s=end), text=label) subs.append(ev) # FFMpeg does not like empty subtitles if len(subs) == 0: subs.append( SSAEvent(start=make_time(s=0), end=make_time(s=0), text="empty")) # Store as ass file # NOTE: Use srt here, as pysubs2 uses 10h timestamp limit # MKV convert to ass is not limited to 10h tmpSubFileName = os.path.join(WORKING_FOLDER, "tmp_sub." + subFormat) subs.save(tmpSubFileName, encoding="utf-8") returnedFile = tmpSubFileName goalFilePath = os.path.join(WORKING_FOLDER, "subbed_" + os.path.basename(filePath)) if dataFormat == "mkv": tmpFiles.append(tmpSubFileName) wsManager.sendStatus(sessionID, "Converting labels to MKV..", percent=85) tmpSubMKVFileName = os.path.join(WORKING_FOLDER, "tmp_sub.mkv") # Convert to MKV and set correct title systemCall = "ffmpeg -hide_banner -i {} {} -y {}".format( tmpSubFileName, mkv.makeMetaArgs(dataDict["title"], "subtitle"), tmpSubMKVFileName) mkv.__call_ffmpeg(systemCall, verbose=DEBUG_FFMPEG) returnedFile = tmpSubMKVFileName if includeData: tmpFiles.append(tmpSubMKVFileName) wsManager.sendStatus(sessionID, "Merging data and labels ...", percent=95) systemCall = "ffmpeg -hide_banner -i {} -i {} -c copy -map_metadata 0 -map 0:{} -map 1:0 -y {}".format( filePath, tmpSubMKVFileName, streamIndex, goalFilePath) mkv.__call_ffmpeg(systemCall, verbose=DEBUG_FFMPEG) returnedFile = goalFilePath # Resonse file filename = os.path.basename(origFileName).split(".")[0] + "." + dataFormat if selRange is not None: filename = "range_" + filename if not includeData: filename = "subs_" + filename chunk_size = 8192 response = StreamingHttpResponse(FileWrapper( open(returnedFile, 'rb'), min(chunk_size, os.path.getsize(returnedFile))), content_type="application/octet-stream") response['Content-Length'] = os.path.getsize(returnedFile) response['Content-Disposition'] = "attachment; filename=%s" % filename for t in tmpFiles: print(t) subprocess.check_output(["rm", "-rf", t]) wsManager.sendTask(sessionID, "dismissProgressbar") return response
async def play(self, ctx, *, url: str = "UNDEFINED"): await self.bot.safe_delete(ctx.message) permCheck, err = (await self.bot.permissions_hasrole(self, ctx.message.author, "music")) if permCheck: if (self.bot.ismuted): await ctx.send(embed=(await self.bot.generate_error(self, ":mute: The speakers have been muted by an Admin")), delete_after=20) return if (self.bot.videoPlaying): currentMins = int(int(self.bot.videoCurrentTimer) / 60); currentSecs = int(int(self.bot.videoCurrentTimer) - int(currentMins * 60)) await ctx.send(embed=(await self.bot.generate_error(self, "Can't play a song since another is already playing!")), delete_after=20) return if (url == "UNDEFINED"): await ctx.send(embed=(await self.bot.generate_error(self, "Incorrect usage. Please use: ``" + self.bot.config["prefix"] + "play youtubeurl``.\nFor example: ``" + self.bot.config["prefix"] + "play https://www.youtube.com/watch?v=wDgQdr8ZkTw``\n\nOr, You can use ``" + self.bot.config["prefix"] + "play video search``.\nFor example: ``" + self.bot.config["prefix"] + "play megalovania OST``")), delete_after=20) # Megalovania because obvs return deleteMessage = await ctx.send(":hourglass_flowing_sand: Searching for song...") skipVideoInstance = False # Flags quickSearch = False if ("-qs" in url.lower()): quickSearch = True; url = url.replace("-qs", ""); print("[SOUND][PLAY] -qs Flag active") if ("youtub.be" in url.lower()): # mobile link print("[SOUND][PLAY] Was Youtu.be mobile link, Formatting into Desktop.") ending = (url.split("tu.be/"))[1] url = "https://youtube.com/watch?v=" + ending if (not ("youtube.com/watch?v=" in url)): # Do search print("[SOUND][PLAY] Getting urls for searchterm: " + str(url)) urls = await self.searchforvideo(url) if (urls[0] == None): await self.bot.safe_delete(deleteMessage) await ctx.send(embed=(await self.bot.generate_error(self, "Couldn't find ``" + url + "``. Try changing your search terms?")), delete_after=20) return if quickSearch: correct = str(urls[0]) urls = [] urls.append(str(correct)) urls.append(None) # Dumb, but still a good skip if not (urls[1] == None): try: await deleteMessage.edit(content=":hourglass_flowing_sand: Gathering song data...") except Exception: pass # Show options urlTitles = []; lyricString = [] blocked = [] i=0 vidUnavailable = False print("[SOUND][PLAY] Loading into ytInstance checker.") ytInstances = [] for url in urls: try: try: ytInstance = YouTube(url) urlTitles.append(str(ytInstance.title)) ytInstances.append(ytInstance) except pytube.exceptions.RegexMatchError: await self.bot.safe_delete(deleteMessage) await ctx.send(embed=(await self.bot.generate_error(self, "Failed to get URL correctly. Try again?")), delete_after=20) return i+=0 except pytube.exceptions.VideoUnavailable: blocked.append(i) vidUnavailable = True if vidUnavailable: print("[SOUND][PLAY] Video Unavailable, Skipping ask") if len(urlTitles) > 0: url = urls[0] else: await self.bot.safe_delete(deleteMessage) await ctx.send(embed=(await self.bot.generate_error(self, "Couldn't get any valid links")), delete_after=20) return else: for captionCheck in ytInstances: captions = captionCheck.captions.get_by_language_code('en') if captions is None: lyricString.append(" ") else: lyricString.append(" (**Lyrics Found**) ") await self.bot.safe_delete(deleteMessage) embedtext = "" embedtext += ":one: ``" + urlTitles[0] + "``" + lyricString[0] + "\n" embedtext += ":two: ``" + urlTitles[1] + "``" + lyricString[1] + "\n" embedtext += ":three: ``" + urlTitles[2] + "``" + lyricString[2] + "\n" embedtext += "\n:octagonal_sign: ``None (Stop)``\n" embed=discord.Embed(title="Found a few videos...", color=(await self.bot.generate_embedcolor()), description=embedtext) embed.set_footer(text="If you don't choose in 20 seconds, I'll assume you dont want any") embedopt = await ctx.send(embed=embed, delete_after=20) await embedopt.add_reaction("1️⃣") await embedopt.add_reaction("2️⃣") await embedopt.add_reaction("3️⃣") await embedopt.add_reaction("🛑") def check(react, user): return user == ctx.message.author and (str(react.emoji) in ["1️⃣", "2️⃣", "3️⃣", "🛑"]) failed = False try: reaction, user = await self.bot.wait_for("reaction_Add", timeout=20, check=check) except Exception: failed = True return await self.bot.safe_delete(embedopt) if (reaction is None): failed = True if failed: await ctx.send(embed=(await self.bot.generate_error(self, "I dont have all day, And you took too long. Try to run the same command but be a bit snappier?")), delete_after=20) return reaction = reaction.emoji # Get emoji print("[SOUND][PLAY] User chose reaction: " + reaction) if (reaction == "1️⃣"): url = urls[0]; ytInstance = ytInstances[0] elif (reaction == "2️⃣"): url = urls[1]; ytInstance = ytInstances[1] elif (reaction == "3️⃣"): url = urls[2]; ytInstance = ytInstances[2] elif (reaction == "🛑"): # Quit await ctx.send(embed=(await self.bot.generate_embed(self, title="You decided to Quit by pressing '🛑'. Well... Cya!", footer=f"For {ctx.message.author.name}")), delete_after=20) return else: await ctx.send(embed=(await self.bot.generate_error(self, "I dont have all day, And you took too long. Try to run the same command but be a bit snappier?")), delete_after=20) return deleteMessage = await ctx.send(":hourglass_flowing_sand: Loading song data...") skipVideoInstance = True else: url = urls[0] if not skipVideoInstance: try: ytInstance = YouTube(url) except pytube.exceptions.RegexMatchError: # Invalid URL await self.bot.safe_delete(deleteMessage) await ctx.send(embed=(await self.bot.generate_error(self, "The YouTube URL you entered was not valid.")), delete_after=20) return mins = int(int(ytInstance.length) / 60); seconds = int(int(ytInstance.length) - int(mins * 60)) if (int(ytInstance.length) > (6 * 60)): permCheck, err = (await self.bot.permissions_isowner(self, ctx.message.author)) # If its longer than 5 minutes if not permCheck: permCheck, err = (await self.bot.permissions_hasrole(self, ctx.message.author, "bypassmusiclimit")) if not permCheck: await self.bot.safe_delete(deleteMessage) await ctx.send(embed=(await self.bot.generate_error(self, f"Your song is too long. The longest you can play is ``5:00`` minutes.\nYour song is ``{mins}:{seconds}``.")), delete_after=20) return if (ytInstance.age_restricted): permCheck, err = (await self.bot.permissions_hasrole(self, ctx.message.author, "admin")) # If its age restricted if not permCheck: await self.bot.safe_delete(deleteMessage) await ctx.send(embed=(await self.bot.generate_error(self, "Your video (||" + (ytInstance.title) + "||) is age restricted, And cannot be played.\nIf you belive this to be an error please contact an Admin who can play the song for you.")), delete_after=20) return captions = ytInstance.captions.get_by_language_code('en') subtitles = [] if not captions is None: print("[SOUND][PLAY] Captions exist") self.bot.subtitlesexist = True captions = captions.generate_srt_captions() with open(self.bot.config["rootpath"] + "/data/subtiles.srt", "w") as fp: fp.write(captions) # Dump subtiles, mainly for debug pysubs2.make_time(s=1) subs = pysubs2.load(self.bot.config["rootpath"] + "/data/subtiles.srt") subtitles = [] for i in range(len(subs)): subtitles.append(subs[i].text) else: print("[SOUND][PLAY] Captions don't exist") self.bot.subtitlesexist = False subs = None self.bot.videoData["captions"] = subtitles self.bot.videoData["thumbnail"] = str(ytInstance.thumbnail_url) self.bot.videoData["title"] = str(ytInstance.title) self.bot.videoData["length"] = int(ytInstance.length) self.bot.videoData["views"] = int(ytInstance.views) self.bot.videoData["description"] = str(ytInstance.description) self.bot.videoData["rating"] = str(ytInstance.rating) self.bot.videoData["lengthString"] = str(str(mins) + ":" + str(seconds)) self.bot.videoData["requester"] = ctx.message.author streams = (ytInstance.streams.filter(only_audio=True)) path = (self.bot.config["rootpath"] + "/data/") try: await deleteMessage.edit(content=":hourglass_flowing_sand: Downloading song...") except Exception: pass print("[SOUND][PLAY] About to download: " + str(ytInstance.title)) emojis = ["hourglass", "hourglass_flowing_sand"] emojiCounter = 0 #streams.first().download(output_path=path, filename="video") # Actually do download if ((pathlib.Path(self.bot.config["rootpath"] + "/data/downloaded.txt")).exists()): os.system("rm -f " + self.bot.config["rootpath"] + "/data/downloaded.txt") os.system("lxterminal -e python3 " + self.bot.config["rootpath"] + "/utils/downloadsong.py '"+url+"' '" + path + "'") maxCount = 0 print("[SOUND][PLAY] Entering waiting loop for install") while (not ((pathlib.Path(self.bot.config["rootpath"] + "/data/downloaded.txt")).exists())): if maxCount > 60: await self.bot.safe_delete(deleteMessage) await ctx.send(embed=(await self.bot.generate_error(self, "Song took too long to install.")), delete_after=20) return maxCount += 1 if emojiCounter == 0: emoji = emojis[0] emojiCounter = 1 else: emoji = emojis[1] emojiCounter = 0 try: await deleteMessage.edit(content=":" + emoji + ": Downloading song...\nGive up in: ``" + str(maxCount) + "/60``") except Exception: pass await asyncio.sleep(1) fileContent = "" with open(self.bot.config["rootpath"] + "/data/downloaded.txt", "r") as f: fileContent = str((f.read()).replace("\n", "")) print("[SOUND][PLAY] Got back: " + fileContent) if (fileContent == "0"): await self.bot.safe_delete(deleteMessage) await ctx.send(embed=(await self.bot.generate_error(self, "Couldn't complete install even after it was pushed to the download handle.")), delete_after=20) return # Exists os.system("rm -f " + self.bot.config["rootpath"] + "/utils/downloaded.txt") await self.bot.safe_delete(deleteMessage) if not ((pathlib.Path(self.bot.config["rootpath"] + "/data/video.mp4")).exists()): await ctx.send(embed=(await self.bot.generate_error(self, "Failed to download the song correctly. This is usually YouTubes fault. Please retry.")), delete_after=20) return embed=discord.Embed(title=self.bot.videoData["title"], color=(await self.bot.generate_embedcolor())) embed.set_image(url=self.bot.videoData["thumbnail"]) embed.add_field(name="Length", value=f"{mins}:{seconds}", inline=True) embed.add_field(name="Views", value=str(self.bot.videoData["views"]), inline=True) if (self.bot.subtitlesexist): embed.add_field(name="Lyrics", value="You're lucky, It just so happens that the person / people who made this song went out of their way to include Lyric Captions on the video!\n\nUse ``" + self.bot.config["prefix"] + "lyrics`` to view the assosiated lyrics.", inline=False) else: embed.add_field(name="Lyrics", value="Sadly the maker of this song didn't bother to create Lyric Captions so I cant get em' for you. My appologies.", inline=False) embed.set_footer(text="Now Playing...") await ctx.send(embed=embed, delete_after=20) activity = discord.Activity(name=(self.bot.videoData["title"]), type=discord.ActivityType.listening) await self.bot.change_presence(activity=activity) self.bot.videoPlaying = True self.bot.videoCurrentTimer = 0 # Important part os.system("lxterminal -e vlc --audio "+ self.bot.config["rootpath"] +"/data/video.mp4") print("[SOUND][PLAY] Starting "+ str(ytInstance.length) +" second song timer for " + ytInstance.title) for i in range(int(ytInstance.length) + 2): # Do waiting loop, 2 second buffer await asyncio.sleep(1) self.bot.videoCurrentTimer = self.bot.videoCurrentTimer + 1 if not self.bot.videoPlaying: break print(Fore.GREEN + "[SOUND][PLAY] Finished song timer for " + ytInstance.title + Style.RESET_ALL) await self.bot.killmusic(self) # Kill all music else: await ctx.send(embed=(await self.bot.generate_error(self, err)), delete_after=20) return
def createEvent(content,start, end, ): ev = pysubs2.SSAEvent(start=pysubs2.make_time(s=start), end=pysubs2.make_time(s=end), text=content) return ev
def test_repr_dialogue(): ev = SSAEvent(start=make_time(m=1, s=30), end=make_time(m=1, s=35), text="Hello\\Nworld!") ref = r"<SSAEvent type=Dialogue start=0:01:30 end=0:01:35 text='Hello\\Nworld!'>" assert repr(ev) == ref
def test_repr_comment(): ev = SSAEvent(start=make_time(m=1, s=30), end=make_time(m=1, s=35), text="Hello\\Nworld!") ev.is_comment = True ref = "<SSAEvent type=Comment start=0:01:30 end=0:01:35 text='Hello\\Nworld!'>" assert repr(ev) == ref
def test_repr_dialogue(): ev = SSAEvent(start=make_time(m=1, s=30), end=make_time(m=1, s=35), text="Hello\\Nworld!") ref = "<SSAEvent type=Dialogue start=0:01:30 end=0:01:35 text='Hello\\Nworld!'>" assert repr(ev) == ref
cv.imwrite( "{}/f{}_l{}.jpg".format(frame_dir, fc_start, fc - fc_start), last_srt_frame) if output_segged_frame: cv.imwrite( "{}/f{}_l{}_segged.jpg".format( frame_dir, fc_start, fc - fc_start), last_srt_seg_frame) srt_count += 1 fc_start = 0 progress_bar.setValue(int(fc / frames_num * 100) + 1) # progress_bar.format(Q) elapsed = time.time() - time_start eta = (frames_num - fc) * elapsed / fc if fc > 0 else 0 print('[%d/%d] Elapsed: %s, ETA: %s' % (fc, frames_num, fmt_time(elapsed), fmt_time(eta))) video_path.split('/') subs.save(frame_dir + '/split_vision.ass') progress_bar.setValue(100) QMessageBox.information(main_window, "提示", "时间轴生成成功!", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) progress_bar.setValue(0) if __name__ == "__main__": sub = SSAFile.load('demo/empty.ass') sub.append( SSAEvent(start=0, end=make_time(s=2.5), text="New first subtitle")) print("test")