-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
201 lines (176 loc) · 9.22 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import os
import sys
import glob
import argparse
try:
import cPickle as pickle
except ImportError:
import pickle
import logging
import collections
import typing
LOGGING_FORMAT = '%(asctime)s|%(levelname)s|%(message)s'
logging.basicConfig(format=LOGGING_FORMAT, level=logging.INFO)
from recode.helpers import NUM_THREADS, which, get_suffix, open_with_dir, ensuredir, confirm_yesno
from recode.tasks import Executor
from recode.media.parsers import PARSERS, ALL_PARSERS, UPCAST
from recode.media.base import UnknownFile, BadParameters, MediaEntry
from recode.locked_state import LockedState
def parse_fentry(fentry: typing.Tuple[str, str], suffix: str, forced_parser: MediaEntry=None, forced_params: dict=None, target_quality: str='') -> MediaEntry:
fname, fpath = fentry
if not suffix:
fname = os.path.splitext(fname)[0]
else:
if not fname.endswith(suffix):
raise ValueError('Bad name "%s" - not ending in suffix "%s"' % (fname, suffix))
fname = fname[:-len(suffix)]
if forced_parser:
return [forced_parser.parse_forced(fname, fpath, forced_params)]
for parser in PARSERS:
try:
entry = parser.parse(fname, fpath)
except UnknownFile:
continue
else:
try:
upcasters = UPCAST[entry.FORCE_NAME][target_quality]
except KeyError:
return [entry]
return [p.parse(fname, fpath) for p in upcasters]
raise ValueError('Cannot parse "%s" - no handlers found' % fname)
def get_files(src_list):
STUB = r'''/external/path1/Series Name.S01E01.Episode name 1.suffix.mkv
/external/path1/Series Name.S01E02.Episode name 2.suffix.mkv
/external/path1/Series Name.S01E03.Episode name 3.suffix.mkv
/external/path1/Series Name.S01E04.Episode name 4.suffix.mkv
'''.splitlines()
lst = STUB if sys.platform == 'win32' else src_list
return tuple((os.path.basename(fname), fname) for fname in lst)
def main():
upcast_choices = set()
for val in UPCAST.values():
upcast_choices |= set(val)
parser = argparse.ArgumentParser(description='Transcode some videos for storing')
parser.add_argument('source', metavar='SRC_PATH_LIST', type=str, nargs='*', help='Source items to compress')
parser.add_argument('--dest', metavar='DEST_PATH', type=str, default='', help='Path to target directory for this type of content (e.g. not including series name)')
parser.add_argument('--resume', action='store_true', help='Resume unfinished recoding')
parser.add_argument('--state', metavar='STATE_FILENAME', type=str, default='', help='Path to file where state to be stored')
parser.add_argument('--log', metavar='LOG_FILENAME', type=str, default='', help='Path to append logs to')
parser.add_argument('--nostart', action='store_true', help='Do not start encoding, just create state file for resuming later')
parser.add_argument('--debug', action='store_true', help='Produce some additional debug output')
parser.add_argument('--scriptize', action='store_true', help='Only generate shell scripts for encoding, do no real encoding work')
parser.add_argument('--interactive', '-i', action='store_true', help='Be interactive: ask some questions before running')
parser.add_argument('--drop-video', action='store_true', help='Drop video stream altogether')
parser.add_argument('--target-quality', choices=sorted(upcast_choices), default='default', help='Enforce quality of target if supported')
parser.add_argument('--force-type', choices=[media_parser.FORCE_NAME for media_parser in ALL_PARSERS], help='Force media type')
parser.add_argument('--force-params', type=str, default='', help='Additional parameters for forced media type')
parser.add_argument('--list-params', action='store_true', help='Show parameters accepted by each media type')
args = parser.parse_args()
if args.list_params:
print('Accepted parameters to be passed via --force-params:')
for media_parser in ALL_PARSERS:
print('[--force-type = %s]' % media_parser.FORCE_NAME)
params = media_parser.describe_parameters()
maxkeylen = max(len(p.key) for p in params)
maxkindlen = max(len(p.kind) for p in params)
grouped = collections.defaultdict(list)
for param in params:
grouped[param.group].append(param)
for group, params in sorted(grouped.items()):
print('Group "%s":' % (group or 'general'))
for param in sorted(params, key=lambda p: p.key):
print('\t%s [%s]%s' % (param.key.ljust(maxkeylen), param.kind.center(maxkindlen), ' - %s' % param.help if param.help else ''))
return
if args.interactive and args.resume:
parser.print_help()
sys.exit('Cannot be interactive and resume at the same time')
forced_parser, forced_params = None, None
if args.force_type:
forced_parser = [media_parser for media_parser in ALL_PARSERS if media_parser.FORCE_NAME == args.force_type][0]
if args.force_params:
try:
forced_params = forced_parser.parse_parameters(args.force_params, targets_multiple_sources=len(args.source) > 1)
except BadParameters as err:
sys.exit('Incorrect parameters for "%s" media type: %s' % (args.force_type, err.msg))
if not args.state:
if not args.dest:
parser.print_help()
sys.exit("Please specify either dest path or --state")
resume_file = os.path.abspath(os.path.join(args.dest, 'tasks.pickle'))
else:
resume_file = os.path.abspath(args.state)
state = LockedState(resume_file)
if args.log or args.dest:
logpath = os.path.abspath(args.log or os.path.join(args.dest, 'recode.log'))
ensuredir(os.path.dirname(logpath))
handler = logging.FileHandler(logpath, delay=True)
handler.setFormatter(logging.Formatter(LOGGING_FORMAT))
logging.getLogger().addHandler(handler)
if args.debug:
logging.getLogger().setLevel(logging.DEBUG)
if not args.resume:
if not args.source or not args.dest:
parser.print_help()
sys.exit('You must specify at least one source item and DEST_PATH when running without --resume')
inp = get_files([os.path.abspath(f) for f in args.source])
logging.info('Found %d items' % len(inp))
suffix = ''
entries = []
entry_types = set()
for fentry in inp:
got = parse_fentry(fentry, suffix, forced_parser, forced_params, args.target_quality)
logging.debug('Parsed entry "%s"' % got[0].full_name)
entries.extend(got)
entry_types |= set(type(entry) for entry in got)
need_reparse = False
if not forced_parser and args.force_params:
if len(entry_types) != 1:
sys.exit('Cannot force params when multiple media types found')
forced_parser = list(entry_types)[0]
try:
forced_params = forced_parser.parse_parameters(args.force_params, targets_multiple_sources=len(args.source) > 1)
except BadParameters as err:
sys.exit('Incorrect parameters for "%s" media type: %s' % (forced_parser.FORCE_NAME, err.msg))
# re-parse entries
logging.debug('Re-parsing entries as forced params detected')
need_reparse = True
if len(entry_types) == 1 and list(entry_types)[0].STRIP_SUFFIX:
suffix = get_suffix(inp)
logging.info('Detected suffix as "%s", re-parsing entries' % suffix)
need_reparse = True
if need_reparse:
entries = []
for fentry in inp:
got = parse_fentry(fentry, suffix, forced_parser, forced_params, args.target_quality)
logging.debug('Parsed entry "%s"' % got[0].full_name)
entries.extend(got)
entries.sort(key=lambda fe: fe.comparing_key)
if args.interactive:
for entry in entries:
entry.interact()
new_tasks = [entry.make_encode_tasks(os.path.abspath(args.dest), logpath or None, args.drop_video) for entry in entries]
with state:
try:
tasks = state.read()
except IOError:
tasks = []
logging.info('Resume file "%s" does not exist, starting from scratch' % resume_file)
state_existed = False
else:
logging.info('Resume file "%s" exists, appending' % resume_file)
state_existed = True
tasks.extend(new_tasks)
state.write(tasks)
if args.scriptize:
logging.info('Scriptizing started')
Executor(state, scriptize=True).execute()
logging.info('Scriptizing stopped')
elif not args.nostart:
if not args.resume and state_existed and not confirm_yesno('State file already exists, are you sure encoding is not running in the background', False):
logging.info('State file already exists, probably recoding is running in the background. Appended new tasks, now exiting')
return
logging.info('Recoding started')
Executor(state).execute()
logging.info('Recoding stopped')
if __name__ == '__main__':
main()