Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 129a948f02 | |||
| 34a99fdec0 | |||
| 15db01096f | |||
| 588fa17eb2 | |||
| b8326b9c73 | |||
| 93fe7d3fd7 | |||
| 5bb2e7cb0b | |||
| 98e4a79a4d | |||
| 811aceae81 | |||
| 605676b13e | |||
| 6d1d2bdc4f | |||
| b1182e88a0 | |||
| 447973e773 | |||
| 983d302eaa | |||
| c1c2a424f4 | |||
| 64fba5c94c | |||
| a2ea5cbd28 |
@@ -30,28 +30,30 @@ Here's a quick demo how to set up and run ```videogrep.bat``` on Windows ([YouTu
|
||||
|
||||
### Additional Options:
|
||||
|
||||
* ```-ph GAP_BETWEEN_PHRASES, --phrases```
|
||||
move the start time of the clip to the beginning of the current phrase (default=1.25 seconds)
|
||||
* ```-l SECONDS, --limit```
|
||||
maximum phrase's duration (default=60 seconds)
|
||||
* ```-p SECONDS, --padding```
|
||||
padding in seconds to add to the start and the end of each clip (default=0.0 seconds)
|
||||
* ```-e SECONDS, --ending```
|
||||
play only matching lines (or phrases)
|
||||
* ```-r, --randomize```
|
||||
randomize the clips
|
||||
* ```-o FILENAME, --output```
|
||||
write the \'grep\' output to the file
|
||||
* ```-d, --demo```
|
||||
only show grep results
|
||||
* ```-a, --audio```
|
||||
create audio fragments
|
||||
* ```-v, --video```
|
||||
create video fragments
|
||||
* ```-s, --subtitles```
|
||||
create subtitles
|
||||
* ```-vs, --video-sub```
|
||||
create video fragments with hardcoded subtitles
|
||||
* ```-s, --subtitles```
|
||||
create subtitles for fragments
|
||||
* ```-o DIRNAME, --output```
|
||||
the output folder for audio and video fragments (default=.)
|
||||
* ```-d, --demo```
|
||||
only show grep results
|
||||
* ```-g FILENAME, --grep-output```
|
||||
write the \'grep\' output to the file
|
||||
* ```-ph GAP_BETWEEN_PHRASES, --phrases```
|
||||
move the start time of the clip to the beginning of the current phrase (default=1.25 seconds)
|
||||
* ```-l SECONDS, --limit```
|
||||
maximum phrase\'s duration (default=60 seconds)
|
||||
* ```-m OPTIONS, --mpv-options```
|
||||
mpv player options
|
||||
|
||||
@@ -88,8 +90,8 @@ Here's example video how it looks like (YouTube):
|
||||
|
||||
# Requirements
|
||||
|
||||
* python 2.7
|
||||
* grep
|
||||
* python 3
|
||||
* grep or [ripgrep](https://github.com/BurntSushi/ripgrep) (for non-ASCII languages)
|
||||
* mpv
|
||||
* ffmpeg
|
||||
|
||||
|
||||
+83
-35
@@ -4,6 +4,7 @@
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
@@ -163,7 +164,14 @@ def get_fragment_filename(phrase):
|
||||
s = s[:max_filename_length] + "..."
|
||||
return re.sub(r'(?u)[^-\w\'\.]', '', s)
|
||||
|
||||
def create_fragments(search_phrase, clips, export_mode):
|
||||
def subprocess_call(args):
|
||||
try:
|
||||
output = subprocess.check_output(args, stderr=subprocess.STDOUT, universal_newlines=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("\n\n", e.output)
|
||||
sys.exit(1)
|
||||
|
||||
def create_fragments(search_phrase, clips, export_mode, output_dir):
|
||||
idx = 1
|
||||
|
||||
update_progress(0, 0, len(clips))
|
||||
@@ -173,23 +181,23 @@ def create_fragments(search_phrase, clips, export_mode):
|
||||
if len(clips) > 1:
|
||||
fragment_filename += "_" + str(idx).zfill(3)
|
||||
|
||||
fragment_filename = os.path.join(output_dir, fragment_filename)
|
||||
|
||||
ss = clip_start
|
||||
to = clip_end
|
||||
t = to - ss
|
||||
|
||||
t_fade = 0.2
|
||||
af = "afade=t=in:st=%s:d=%s,afade=t=out:st=%s:d=%s" % (0, t_fade, t - t_fade, t_fade)
|
||||
video_encoding_mode = "ultrafast"
|
||||
video_encoding_mode = "medium"
|
||||
|
||||
if export_mode["audio"]:
|
||||
cmd = " ".join(["ffmpeg", "-y", "-ss", str(ss), "-i", '"' + video_file + '"', "-loglevel", "quiet", "-t", str(t), "-map", "0:a:0", "-af", af, '"' + fragment_filename + ".mp3" + '"'])
|
||||
p = subprocess.Popen(cmd)
|
||||
p.wait()
|
||||
cmd = ["ffmpeg", "-y", "-ss", str(ss), "-i", video_file, "-t", str(t), "-af", af, fragment_filename + ".mp3"]
|
||||
subprocess_call(cmd)
|
||||
|
||||
if export_mode["video"]:
|
||||
cmd = " ".join(["ffmpeg", "-y", "-ss", str(ss), "-i", '"' + video_file + '"', "-strict", "-2", "-loglevel", "quiet", "-t", str(t), "-map", "0:v:0", "-map", "0:a:0", "-c:v", "libx264", "-preset", video_encoding_mode, "-c:a", "aac", "-ac", "2", "-af", af, '"' + fragment_filename + ".mp4" + '"'])
|
||||
p = subprocess.Popen(cmd)
|
||||
p.wait()
|
||||
cmd = ["ffmpeg", "-y", "-ss", str(ss), "-i", video_file, "-strict", "-2", "-t", str(t), "-map", "0:v", "-map", "0:a", "-c:v", "libx264", "-preset", video_encoding_mode, "-c:a", "aac", "-ac", "2", "-af", af, fragment_filename + ".mp4"]
|
||||
subprocess_call(cmd)
|
||||
|
||||
if export_mode["video-sub"]:
|
||||
srt_style = "FontName=Arial,FontSize=22"
|
||||
@@ -201,12 +209,11 @@ def create_fragments(search_phrase, clips, export_mode):
|
||||
srt_filename = srt_filename.replace(",", "\\\\\\,")
|
||||
srt_filename = srt_filename.replace("'", "\\\\\\'")
|
||||
|
||||
vf = "\"" + "subtitles=" + srt_filename + ":force_style='" + srt_style + "',setpts=PTS-STARTPTS" + "\""
|
||||
vf = "subtitles=" + srt_filename + ":force_style='" + srt_style + "',setpts=PTS-STARTPTS"
|
||||
af = "afade=t=in:st=%s:d=%s,afade=t=out:st=%s:d=%s,asetpts=PTS-STARTPTS" % (ss, t_fade, to - t_fade, t_fade)
|
||||
|
||||
cmd = " ".join(["ffmpeg", "-y", "-ss", str(ss), "-i", '"' + video_file + '"', "-strict", "-2", "-loglevel", "quiet", "-t", str(t), "-map", "0:v:0", "-map", "0:a:0", "-c:v", "libx264", "-preset", video_encoding_mode, "-c:a", "aac", "-ac", "2", "-vf", vf, "-af", af, "-copyts", '"' + fragment_filename + ".sub.mp4" + '"'])
|
||||
p = subprocess.Popen(cmd)
|
||||
p.wait()
|
||||
cmd = ["ffmpeg", "-y", "-ss", str(ss), "-i", video_file, "-strict", "-2", "-t", str(t), "-map", "0:v", "-map", "0:a", "-c:v", "libx264", "-preset", video_encoding_mode, "-c:a", "aac", "-ac", "2", "-vf", vf, "-af", af, "-copyts", fragment_filename + ".sub.mp4"]
|
||||
subprocess_call(cmd)
|
||||
|
||||
if export_mode["subtitles"]:
|
||||
subtitles_filename = video_file.rsplit('.', 1)[0] + ".srt"
|
||||
@@ -236,6 +243,7 @@ def play_clips(clips, ending_mode, mpv_options):
|
||||
with open(pipe_name, "wb", 0) as f_pipe:
|
||||
for clip_filename, clip_start, clip_end in clips:
|
||||
clip_filename = clip_filename.replace("\\","/")
|
||||
clip_filename = clip_filename.replace("\"", "\\\"")
|
||||
|
||||
cmd = ["loadfile", '"' + clip_filename + '"']
|
||||
if ending_mode:
|
||||
@@ -256,25 +264,50 @@ def play_clips(clips, ending_mode, mpv_options):
|
||||
p.kill()
|
||||
return
|
||||
|
||||
def main(media_dir, search_phrase, phrase_mode, phrases_gap, padding, limit, output_file, ending_mode, randomize_mode, demo_mode, mpv_options, audio_mode, video_mode, video_with_sub_mode, subtitles_mode):
|
||||
search_phrase_in_grep = "\"(?s)\(\d\d:\d\d:\d\d,\d\d\d\, \d\d:\d\d:\d\d,\d\d\d\)\\t[^\\n]*" + search_phrase + "[^\\n]*\""
|
||||
def print_match(media_dir, filename, line, attrs={"prev_filename": None}):
|
||||
if filename.startswith(media_dir):
|
||||
filename = filename.replace(media_dir + os.sep, '', 1)
|
||||
|
||||
cmd = " ".join(["grep", "-r", "-z", "-o", "-i", "--include", "*.txt", "-P", search_phrase_in_grep, '"' + media_dir + '"'])
|
||||
if attrs["prev_filename"] != filename:
|
||||
print()
|
||||
print('-', filename)
|
||||
print()
|
||||
|
||||
attrs["prev_filename"] = filename
|
||||
|
||||
line = line.replace('\t', ' ')
|
||||
print(line)
|
||||
|
||||
def main(media_dir, search_phrase, phrase_mode, phrases_gap, padding, limit, output_dir, grep_file, ending_mode, randomize_mode, demo_mode, mpv_options, audio_mode, video_mode, video_with_sub_mode, subtitles_mode):
|
||||
search_phrase_in_grep = "(?s)\(\d\d:\d\d:\d\d,\d\d\d\, \d\d:\d\d:\d\d,\d\d\d\)\\t[^\\n]*" + search_phrase + "[^\\n]*"
|
||||
|
||||
rg = shutil.which('rg')
|
||||
if rg:
|
||||
cmd = ["rg", "--sort-files", "--no-heading", "--null-data", "-N", "-o", "-i", "-g", "*.txt", "-P", search_phrase_in_grep, media_dir]
|
||||
else:
|
||||
cmd = ["grep", "-r", "-z", "-o", "-i", "--include", "*.txt", "-P", search_phrase_in_grep, media_dir]
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, bufsize=-1)
|
||||
output, error = p.communicate()
|
||||
|
||||
media_dir = os.path.abspath(media_dir).replace('\\', '/').replace('/', os.sep)
|
||||
|
||||
if p.returncode == 0:
|
||||
matches = output.rstrip("\x00").split("\x00")
|
||||
|
||||
if output_file != None:
|
||||
with open(output_file, 'w') as f_results:
|
||||
|
||||
if grep_file != None:
|
||||
with open(grep_file, 'w') as f_results:
|
||||
f_results.write("\n".join(matches))
|
||||
|
||||
clips = []
|
||||
for match in matches:
|
||||
filename, line = match.split(".txt:", 1)
|
||||
|
||||
lines = line.splitlines()
|
||||
filename, line = match.strip().split(".txt:", 1)
|
||||
|
||||
filename = os.path.abspath(filename).replace('\\', '/').replace('/', os.sep)
|
||||
|
||||
if demo_mode:
|
||||
print_match(media_dir, filename, line)
|
||||
|
||||
lines = line.split('\n')
|
||||
|
||||
def get_line_timings(line):
|
||||
sub_timing, sub_content = line.split("\t", 1)
|
||||
@@ -359,20 +392,23 @@ def main(media_dir, search_phrase, phrase_mode, phrases_gap, padding, limit, out
|
||||
for ext in movie_extensions:
|
||||
movie_filename = filename + "." + ext
|
||||
if os.path.isfile(movie_filename):
|
||||
clips.append((os.path.abspath(movie_filename), phrase_start - padding, phrase_end + padding))
|
||||
clips.append((movie_filename, phrase_start - padding, phrase_end + padding))
|
||||
break
|
||||
|
||||
if demo_mode:
|
||||
print()
|
||||
|
||||
if phrase_mode:
|
||||
print("Number of matches:", len(clips))
|
||||
clips = list(OrderedDict.fromkeys(clips)) # delete dublicates
|
||||
|
||||
|
||||
print("Number of clips:", len(clips))
|
||||
|
||||
if randomize_mode:
|
||||
random.shuffle(clips)
|
||||
|
||||
if audio_mode or video_mode or video_with_sub_mode or subtitles_mode:
|
||||
create_fragments(search_phrase, clips, {"audio": audio_mode, "video": video_mode, "video-sub": video_with_sub_mode, "subtitles": subtitles_mode})
|
||||
create_fragments(search_phrase, clips, {"audio": audio_mode, "video": video_mode, "video-sub": video_with_sub_mode, "subtitles": subtitles_mode}, output_dir)
|
||||
elif not demo_mode:
|
||||
play_clips(clips, ending_mode, mpv_options)
|
||||
|
||||
@@ -430,7 +466,7 @@ def parse_args(argv):
|
||||
print("Search phrase can't be empty")
|
||||
sys.exit(1)
|
||||
|
||||
args = {"padding": 0, "limit": 60, "output_file": None, "phrase_mode": False, "phrases_gap":1.25, "search_phrase":search_phrase, "ending_mode":False, "randomize_mode":False, "demo_mode":False, "mpv_options":"", "audio_mode":False, "video_mode":False, "video_with_sub_mode":False, "subtitles_mode":False }
|
||||
args = {"padding": 0, "limit": 60, "output_dir": ".", "grep_file": None, "phrase_mode": False, "phrases_gap":1.25, "search_phrase":search_phrase, "ending_mode":False, "randomize_mode":False, "demo_mode":False, "mpv_options":"", "audio_mode":False, "video_mode":False, "video_with_sub_mode":False, "subtitles_mode":False }
|
||||
|
||||
argv = argv[:-1]
|
||||
idx = 0
|
||||
@@ -450,10 +486,17 @@ def parse_args(argv):
|
||||
return False
|
||||
args["limit"] = int(argv[idx + 1])
|
||||
idx += 1
|
||||
elif argv[idx] == "--grep-output" or argv[idx] == "-g":
|
||||
if idx + 1 >= len(argv):
|
||||
return False
|
||||
args["grep_file"] = argv[idx + 1]
|
||||
idx += 1
|
||||
elif argv[idx] == "--output" or argv[idx] == "-o":
|
||||
if idx + 1 >= len(argv):
|
||||
return False
|
||||
args["output_file"] = argv[idx + 1]
|
||||
args["output_dir"] = os.path.abspath(argv[idx + 1])
|
||||
if not os.path.exists(args["output_dir"]):
|
||||
os.makedirs(args["output_dir"])
|
||||
idx += 1
|
||||
elif argv[idx] == "--ending" or argv[idx] == "-e":
|
||||
args["ending_mode"] = True
|
||||
@@ -497,9 +540,13 @@ def validate_args(args):
|
||||
if not os.path.isdir(args["media_dir"]):
|
||||
print("ERROR: '{}' is not a folder".format(args["media_dir"]))
|
||||
return False
|
||||
if args["output_file"]:
|
||||
if os.path.isdir(args["output_file"]):
|
||||
print("ERROR: '{}' can't be a folder".format(args["output_file"]))
|
||||
if args["output_dir"]:
|
||||
if os.path.exists(args["output_dir"]) and not os.path.isdir(args["output_dir"]):
|
||||
print("ERROR: '{}' is not a folder".format(args["output_dir"]))
|
||||
return False
|
||||
if args["grep_file"]:
|
||||
if os.path.isdir(args["grep_file"]):
|
||||
print("ERROR: '{}' can't be a folder".format(args["grep_file"]))
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -509,17 +556,18 @@ def print_usage():
|
||||
print("Init: playphrase -i <media_dir> _init_")
|
||||
print()
|
||||
print("Additional options:")
|
||||
print("-ph GAP_BETWEEN_PHRASES, --phrases", " ", "move the start time of the clip to the beginning of the current phrase (default=1.25 seconds)")
|
||||
print("-l SECONDS, --limit", " ", "maximum phrase's duration (default=60 seconds)")
|
||||
print("-p SECONDS, --padding", " ", "padding in seconds to add to the start and the end of each clip (default=0.0 seconds)")
|
||||
print("-e SECONDS, --ending", " ", "play only matching lines (or phrases)")
|
||||
print("-r, --randomize", " ", "randomize the clips")
|
||||
print("-o FILENAME, --output", " ", "write the 'grep' output to the file")
|
||||
print("-d, --demo", " ", "only show grep results")
|
||||
print("-a, --audio", " ", "create audio fragments")
|
||||
print("-v, --video", " ", "create video fragments")
|
||||
print("-s, --subtitles", " ", "create subtitles")
|
||||
print("-vs, --video-sub", " ", "create video fragments with hardcoded subtitles")
|
||||
print("-s, --subtitles", " ", "create subtitles for fragments")
|
||||
print("-o DIRNAME, --output", " ", "the output folder for audio and video fragments (default=.)")
|
||||
print("-d, --demo", " ", "only show grep results")
|
||||
print("-g FILENAME, --grep-output", "", "write the 'grep' output to the file")
|
||||
print("-ph GAP_BETWEEN_PHRASES, --phrases", "", "move the start time of the clip to the beginning of the current phrase (default=1.25 seconds)")
|
||||
print("-l SECONDS, --limit", " ", "maximum phrase's duration (default=60 seconds)")
|
||||
print("-m OPTIONS, --mpv-options", " ", "mpv player options")
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -543,4 +591,4 @@ if __name__ == '__main__':
|
||||
if need_update(args["media_dir"]):
|
||||
print("WARNING: number of '.srt' and '.txt' files doesn't match. Maybe use 'playphrase -i <media_dir> _init_'.")
|
||||
|
||||
main(args["media_dir"], args["search_phrase"], args["phrase_mode"], args["phrases_gap"], args["padding"], args["limit"], args["output_file"], args["ending_mode"], args["randomize_mode"], args["demo_mode"], args["mpv_options"], args["audio_mode"], args["video_mode"], args["video_with_sub_mode"], args["subtitles_mode"])
|
||||
main(args["media_dir"], args["search_phrase"], args["phrase_mode"], args["phrases_gap"], args["padding"], args["limit"], args["output_dir"], args["grep_file"], args["ending_mode"], args["randomize_mode"], args["demo_mode"], args["mpv_options"], args["audio_mode"], args["video_mode"], args["video_with_sub_mode"], args["subtitles_mode"])
|
||||
|
||||
Reference in New Issue
Block a user