Skip to content

Commit f9e749d

Browse files
committed
HEVC-5, HEVC-6, HEVC-7 Adding install/uninstall logic
1 parent 8eb3f98 commit f9e749d

File tree

1 file changed

+144
-61
lines changed

1 file changed

+144
-61
lines changed

transcode.py

Lines changed: 144 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,153 @@
1414
except ImportError:
1515
sys.exit("FATAL: failed to import TranscodeSession from src/TranscodeSession.py\n")
1616

17-
"""
17+
def validate_args():
18+
""" Exits with error messages if command-line arguments are invalid
19+
"""
20+
parser = argparse.ArgumentParser(description="Transcodes given file(s) to HEVC format.")
21+
install_group = parser.add_mutually_exclusive_group()
22+
install_group.add_argument("--install", action="store_true", help="install symlink to transcode.py on $PATH")
23+
install_group.add_argument("--uninstall", action="store_true", help="remove symlink to transcode.py")
24+
files_group = parser.add_mutually_exclusive_group()
25+
files_group.add_argument("--file", help="relative path to movie in source directory")
26+
files_group.add_argument("--all", action="store_true", help="transcode all supported movies in source directory")
27+
parser.add_argument("--quality", type=int, help="HandBrake quality slider value (-12,51)")
28+
parser.add_argument("--preset", help="override video encoder preset")
29+
preset_group = parser.add_mutually_exclusive_group()
30+
preset_group.add_argument("--baseline", action="store_true", help="use baseline encoder options")
31+
preset_group.add_argument("--best", action="store_true", help="use highest quality encoder options")
32+
parser.add_argument("--small", action="store_true", help="use additional encoder options to minimize filesize at the expense of speed")
33+
parser.add_argument("--delete", action="store_true", help="delete output files when complete/interrupted")
34+
args = parser.parse_args()
35+
36+
valid_arguments = False
1837

19-
TODO:
20-
- allow comma-separated string for --preset, e.g. medium,slow,slower, map to list
21-
- add check: if working directory == script location, exit with warning to symlink transcode.py onto $PATH, else if different directory but no symlink, prompt to run --install
22-
- add --install arg (with optional path to custom $PATH location) to create symlink at /usr/local/bin or custom $PATH location?
23-
- once profiling is complete, only append file decorator if --test is specified
38+
if args.install or args.uninstall:
39+
if len(sys.argv) > 2:
40+
print("\nFATAL: --install/--uninstall may not be called with any other arguments")
41+
else:
42+
if args.install:
43+
symlink(True)
44+
else:
45+
symlink(False)
46+
elif os.path.dirname(os.path.realpath(__file__)) == os.getcwd():
47+
print("\nFATAL: invalid working directory: running from master directory. Please create working directory in another location.")
48+
elif not "source" in os.listdir():
49+
print("\nFATAL: invalid working directory: ./source/ does not exist")
50+
elif args.file and not os.path.exists(args.file):
51+
print("\nFATAL:", args.file, "not found!")
52+
elif args.preset and not args.preset.lower() in ("ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", "placebo"):
53+
print("\nFATAL:", args.preset, "not valid!")
54+
elif args.quality and not args.quality in range(-12, 51):
55+
print("\nATAL: quality must be between -12 and 51 (lower is slower + higher quality)")
56+
else:
57+
valid_arguments = True
2458

25-
"""
59+
if not valid_arguments:
60+
sys.exit("Invalid command-line arguments.\n")
61+
elif args.all and args.quality:
62+
print("\nWarning! Combining --all and --quality options is not recommended and may not produce optimal HEVC transcodes.")
63+
proceed = get_user_response()
64+
if not proceed:
65+
sys.exit("Aborting invocation with --all and --quality options.\n")
66+
67+
if not os.path.isdir("performance"):
68+
try:
69+
os.mkdir("performance")
70+
except FileExistsError:
71+
sys.exit("\nFATAL: can't create directory \"performance\" because file with same name exists")
72+
if not os.path.isdir("hevc"):
73+
try:
74+
os.mkdir("hevc")
75+
except FileExistsError:
76+
sys.exit("\nFATAL: can't create directory \"hevc\" because file with same name exists")
77+
78+
return args
79+
80+
def get_user_response():
81+
""" Accepts yes/no answer as user input and returns as boolean
82+
"""
83+
while "need response":
84+
reply = str(input(" Proceed? (y/n) ")).lower().strip()
85+
if len(reply) > 0:
86+
if reply[0] == "y":
87+
response = True
88+
break
89+
if reply[0] == "n":
90+
response = False
91+
break
92+
93+
return response
94+
95+
def symlink(install):
96+
""" Installs / uninstalls a symlink to transcode.py in /usr/local/bin or alternate $PATH location
97+
"""
98+
script_name=os.path.basename(sys.argv[0])
99+
script_realpath = os.path.realpath(__file__)
100+
script_on_path = False
101+
for location in os.get_exec_path():
102+
if script_name in os.listdir(location):
103+
script_on_path = True
104+
script_path_location = os.path.join(location, script_name)
105+
break
106+
107+
if script_on_path:
108+
path_dir = os.path.dirname(script_path_location)
109+
script_executable = os.access(script_realpath, os.X_OK)
110+
111+
if install:
112+
if script_on_path:
113+
sys.exit("\n{script_name} already on $PATH at {script_path_location}, skipping install.\n".format(script_name=script_name, script_path_location=script_path_location))
114+
else:
115+
print("\nCreate symlink for {script_name} on $PATH?".format(script_name=script_name))
116+
proceed = get_user_response()
117+
if proceed:
118+
if not oct(os.stat(script_realpath).st_mode)[-3:] == 755:
119+
os.chmod(script_realpath, 0o755)
120+
print("Use default location? /usr/local/bin")
121+
default_location = get_user_response()
122+
if default_location:
123+
os.symlink(script_realpath, os.path.join("/usr/local/bin", script_name))
124+
sys.exit("Created symlink to {script_name} in /usr/local/bin\n")
125+
else:
126+
print("Use alternate $PATH location?")
127+
alternate_location = get_user_response()
128+
if alternate_location:
129+
alternate_path = str(input("Alternate $PATH location: (case-sensitive) "))
130+
if alternate_path[0] == "~": alternate_path = os.path.expanduser(alternate_path)
131+
if alternate_path in os.get_exec_path():
132+
os.symlink(script_realpath, os.path.join(alternate_path, script_name))
133+
sys.exit("Created symlink to {script_name} in {alternate_path}\n".format(script_name=script_name, alternate_path=alternate_path))
134+
else:
135+
sys.exit("\nError: {alternate_path} not found on $PATH, aborting install.\n".format(alternate_path=alternate_path))
136+
else:
137+
sys.exit("Aborting install.\n")
138+
else:
139+
sys.exit("Aborting install.\n")
140+
else:
141+
if not script_on_path:
142+
sys.exit("\n{script_name} not on $PATH, skipping uninstall.\n".format(script_name=script_name))
143+
else:
144+
print("\nFound {script_name} on $PATH in {path_dir}\n".format(script_name=script_name, path_dir=path_dir))
145+
if os.path.islink(script_path_location):
146+
print("Remove symlink to {script_name} in {path_dir}?".format(script_name=script_name, path_dir=path_dir))
147+
proceed = get_user_response()
148+
if proceed:
149+
os.unlink(script_path_location)
150+
print("Unlinked {script_path_location}\n".format(script_path_location=script_path_location))
151+
else:
152+
sys.exit("Aborting uninstall.\n")
153+
else:
154+
sys.exit("Error: {script_path_location} exists on $PATH but is not a symlink, skipping uninstall.\n".format(script_path_location=script_path_location))
155+
sys.exit()
26156

27157
def build_source_list(args):
28158
""" Constructs and returns list of source files
29159
"""
30160
extensions = [".mp4", ".m4v", ".mov", ".mkv", ".mpg", ".mpeg", ".avi", ".wmv", ".flv", ".webm", ".ts"]
161+
31162
print("\nBuilding source list...")
163+
32164
if args.all:
33165
source_files = ["source/" + file for file in os.listdir("source") if os.path.splitext(file)[1].lower() in extensions]
34166
else:
@@ -44,66 +176,17 @@ def build_source_list(args):
44176
source_files = [file for file in source_files if file is not source_file]
45177

46178
if len(source_files) == 0:
47-
sys.exit("All supported files in ./source/ have already been transcoded. Exiting.\n")
179+
if args.all:
180+
sys.exit("All supported files in ./source/ have already been transcoded. Exiting.\n")
181+
else:
182+
sys.exit("File exists. Exiting.")
48183
else:
49184
print(str(source_files) + "\n")
50185

51186
return source_files
52187

53-
def validate_args(args):
54-
""" Exits with error messages if command-line arguments are invalid
55-
"""
56-
valid_arguments = False
57-
if not "source" in os.listdir():
58-
print("FATAL: invalid working directory!")
59-
elif args.file and not os.path.exists(args.file):
60-
print("FATAL:", args.file, "not found!")
61-
elif args.preset and not args.preset.lower() in ("ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", "placebo"):
62-
print("FATAL:", args.preset, "not valid!")
63-
elif args.quality and not args.quality in range(-12, 51):
64-
print("FATAL: quality must be between -12 and 51 (lower is slower + higher quality)")
65-
else:
66-
valid_arguments = True
67-
68-
if not valid_arguments:
69-
sys.exit("Invalid command-line arguments.\n")
70-
elif args.all and args.quality:
71-
print("Warning! Combining --all and --quality options is not recommended and may not produce optimal HEVC transcodes.")
72-
while "need response":
73-
reply = str(input("Proceed? (y/n)" )).lower().strip()
74-
if reply[0] == "y":
75-
break
76-
if reply[0] == "n":
77-
sys.exit("Aborting invocation with --all and --quality options.\n")
78-
79-
if not os.path.isdir("performance"):
80-
try:
81-
os.mkdir("performance")
82-
except FileExistsError:
83-
sys.exit("FATAL: can't create directory \"performance\" because file with same name exists")
84-
if not os.path.isdir("hevc"):
85-
try:
86-
os.mkdir("hevc")
87-
except FileExistsError:
88-
sys.exit("FATAL: can't create directory \"hevc\" because file with same name exists")
89-
90188
def main():
91-
# Define command-line arguments
92-
parser = argparse.ArgumentParser()
93-
files_group = parser.add_mutually_exclusive_group(required=True)
94-
files_group.add_argument("-f", "--file", help="relative path to movie in source directory")
95-
files_group.add_argument("--all", action="store_true", help="transcode all supported movies in source directory")
96-
parser.add_argument("-q", "--quality", type=int, help="HandBrake quality slider value (-12,51)")
97-
parser.add_argument("--preset", help="override video encoder preset")
98-
preset_group = parser.add_mutually_exclusive_group(required=False)
99-
preset_group.add_argument("--baseline", action="store_true", help="use baseline encoder options")
100-
preset_group.add_argument("--best", action="store_true", help="use highest quality encoder options")
101-
parser.add_argument("--small", action="store_true", help="use additional encoder options to minimize filesize at the expense of speed")
102-
parser.add_argument("--delete", action="store_true", help="delete output files when complete/interrupted")
103-
args = parser.parse_args()
104-
validate_args(args)
105-
106-
# Do the thing
189+
args = validate_args()
107190
source_files = build_source_list(args)
108191
time_script_started = datetime.now()
109192
for file in source_files:

0 commit comments

Comments
 (0)