Skip to content

Commit 7074382

Browse files
committed
Improved source file list creation and tweaked output filenames
1 parent 25da214 commit 7074382

File tree

1 file changed

+47
-46
lines changed

1 file changed

+47
-46
lines changed

transcode.py

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- allow comma-separated string for --preset, e.g. medium,slow,slower, map to list
1818
- add --matrix option to create default, best, small, best+small variants
1919
- add compression ratio / space saved to log + screen output
20+
- ~~if presets.json does not exist, download from github~~
2021
2122
"""
2223

@@ -27,25 +28,25 @@
2728
class Session():
2829
class Settings:
2930
class RF:
30-
SD = 18
31-
HD = 20
32-
FHD = 21
33-
UHD = 24
34-
31+
SD = 21
32+
HD = 22
33+
FHD = 23
34+
UHD = 26
35+
3536
class ENCOPTS:
3637
SD = "ctu=32:qg-size=16"
3738
HD = "ctu=32:qg-size=32"
3839
FHD = "ctu=64:qg-size=64"
3940
UHD = "ctu=64:qg-size=64"
40-
41+
4142
def __init__(self, file):
4243
signal.signal(signal.SIGINT, self.signal_handler)
43-
44+
4445
# Get source file metadata
4546
cmd = "ffprobe -v quiet -print_format json -show_streams " + file
4647
metadata = subprocess.check_output(shlex.split(cmd)).decode("utf-8")
4748
metadata = json.loads(metadata)["streams"][0]
48-
49+
4950
# Populate metadata-based attributes
5051
self.path = {"source": file}
5152
self.source = {"height": int(metadata["height"]), "width": int(metadata["width"]), "duration": float(metadata["duration"]), "filename": os.path.splitext(os.path.relpath(self.path["source"], "source"))[0], "filesize": os.path.getsize(self.path["source"]), "bitrate": int(metadata["bit_rate"]), "frames": int(metadata["nb_frames"]), "codec": metadata["codec_name"]}
@@ -59,46 +60,47 @@ def __init__(self, file):
5960
elif 2160 <= height:
6061
resolution = "UHD"
6162
self.source["resolution"] = resolution
62-
63+
6364
# Create empty attributes for dynamic session options
6465
self.encoder_quality = None
6566
self.encoder_preset = None
6667
self.preset_name = None
6768
self.encoder_options = None
68-
69+
6970
# Construct session options and parameters
7071
self.map_options()
7172
self.file_decorator = "_RF" + str(self.encoder_quality)
72-
if args.best:
73-
self.file_decorator += "_Best"
74-
elif args.baseline:
73+
if args.preset:
74+
self.encoder_preset = args.preset.lower()
75+
self.file_decorator += "_{preset}".format(preset=self.encoder_preset.capitalize())
76+
if args.baseline:
7577
self.file_decorator += "_Baseline"
78+
elif args.best:
79+
self.file_decorator += "_Best"
7680
if args.small:
7781
self.file_decorator += "_Small"
78-
if args.preset:
79-
self.file_decorator += "_{preset}".format(preset=args.preset.capitalize())
8082
self.path["output"] = "hevc/" + self.source["filename"] + self.file_decorator + ".mp4"
8183
self.path["log"] = "performance/" + self.source["filename"] + self.file_decorator + ".log"
82-
84+
8385
# Build HandBrakeCLI command
8486
self.args = "HandBrakeCLI --encoder-preset {encoder_preset} --preset-import-file presets.json --preset {preset_name} --quality {quality} --encopts {encopts} --input {source_path} --output {output_path}".format(encoder_preset=self.encoder_preset, preset_name=self.preset_name, quality=str(self.encoder_quality), encopts=self.encoder_options, source_path=self.path["source"], output_path=self.path["output"])
85-
87+
8688
# Verify no attributes are None
8789
self.validate()
88-
90+
8991
def validate(self):
9092
""" Verifies that no session attributes are null
9193
"""
9294
if any(value is None for attribute, value in self.__dict__.items()):
9395
sys.exit("FATAL: Session.validate(): found null attribute for " + self.path["source"])
94-
96+
9597
def summarize(self):
9698
""" Summarize transcode session before starting
9799
"""
98100
print("{date}: {source}:".format(date=str(datetime.now()), source=self.path["source"]))
99101
pprint(vars(self))
100102
print()
101-
103+
102104
def map_options(self):
103105
""" Start with settings based on source resolution and then override defaults based on command-line arguments
104106
"""
@@ -113,17 +115,17 @@ def map_options(self):
113115
if args.preset:
114116
self.encoder_preset = args.preset.lower()
115117
else:
116-
self.encoder_preset = "slow"
118+
self.encoder_preset = "Slow"
117119
if args.quality:
118120
self.encoder_quality = args.quality
119121
if args.small:
120122
self.encoder_options += ":tu-intra-depth=3:tu-inter-depth=3"
121-
123+
122124
def signal_handler(self, sig, frame):
123125
""" Delete output file if ctrl+c is caught, since file will be corrupt
124126
"""
125127
self.cleanup()
126-
128+
127129
def log(self, elapsed_time, fps):
128130
""" Summarizes transcode session for screen and log
129131
"""
@@ -132,7 +134,7 @@ def log(self, elapsed_time, fps):
132134
logfile.write(summary + "\n\n" + session.args + "\n\n")
133135
pprint(vars(self), logfile)
134136
print(summary)
135-
137+
136138
def cleanup(self):
137139
""" Always deletes output file, deletes log if --delete is passed from command-line
138140
"""
@@ -145,7 +147,7 @@ def cleanup(self):
145147
# Define command-line arguments
146148
parser = argparse.ArgumentParser()
147149
files_group = parser.add_mutually_exclusive_group(required=True)
148-
files_group.add_argument("-f", "--file", help="filename of movie in source directory")
150+
files_group.add_argument("-f", "--file", help="relative path to movie in source directory")
149151
files_group.add_argument("--all", action="store_true", help="transcode all supported movies in source directory")
150152
parser.add_argument("-q", "--quality", type=int, help="HandBrake quality slider value (-12,51)")
151153
parser.add_argument("--preset", help="override video encoder preset")
@@ -183,6 +185,15 @@ def cleanup(self):
183185
extensions = [".mp4", ".m4v", ".mov", ".mkv", ".mpg", ".mpeg", ".avi", ".wmv", ".flv", ".webm", ".ts"]
184186
if args.all:
185187
source_files = ["source/" + file for file in os.listdir("source") if os.path.splitext(file)[1].lower() in extensions]
188+
print(source_files)
189+
for source_file in source_files:
190+
session = Session(source_file)
191+
if os.path.exists(session.path["output"]):
192+
print("Skipping", source_file)
193+
source_files = [file for file in source_files if file is not source_file]
194+
print(source_files)
195+
if len(source_files) == 0:
196+
sys.exit("All source files have already been transcoded. Exiting.")
186197
else:
187198
source_files = [args.file]
188199
if not os.path.exists("performance"):
@@ -192,34 +203,24 @@ def cleanup(self):
192203

193204
# Do the thing
194205
start_time = datetime.now()
195-
skipped_files = []
196206
print("\n{date}: Starting transcode session for {source_files}\n".format(date=str(datetime.now()), source_files=str(source_files)))
197207
for file in source_files:
198208
session = Session(file)
199-
if not os.path.exists(session.path["output"]):
200-
session.summarize()
201-
print(session.args + "\n")
202-
session_start_time = datetime.now()
203-
transcode = subprocess.Popen(shlex.split(session.args, posix=False)) # Posix=False to escape double-quotes in arguments
204-
transcode.wait()
205-
session_end_time = datetime.now()
206-
session_elapsed_time = session_end_time - session_start_time
207-
fps = session.source["frames"] / session_elapsed_time.seconds
208-
print("\n{date}: Finished {output_file}".format(date=str(session_end_time), output_file=session.path["output"]))
209-
session.log(session_elapsed_time, fps)
210-
print("\n\n\n\n\n")
211-
else:
212-
skipped_files.append(file)
209+
session.summarize()
210+
print(session.args + "\n")
211+
session_start_time = datetime.now()
212+
transcode = subprocess.Popen(shlex.split(session.args, posix=False)) # Posix=False to escape double-quotes in arguments
213+
transcode.wait()
214+
session_end_time = datetime.now()
215+
session_elapsed_time = session_end_time - session_start_time
216+
fps = session.source["frames"] / session_elapsed_time.seconds
217+
print("\n{date}: Finished {output_file}".format(date=str(session_end_time), output_file=session.path["output"]))
218+
session.log(session_elapsed_time, fps)
219+
print("\n\n\n\n\n")
213220
if args.delete:
214221
session.cleanup()
215222

216223
end_time = datetime.now()
217224
elapsed_time = end_time - start_time
218225

219-
if len(skipped_files) > 0:
220-
print("Skipped:")
221-
for file in skipped_files:
222-
print(file)
223-
print()
224-
225226
sys.exit("{date}: Finished after {elapsed_time}.\n".format(date=str(datetime.now()), elapsed_time=elapsed_time))

0 commit comments

Comments
 (0)