forked from akent/python-iview
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathiview-cli
executable file
·310 lines (262 loc) · 10 KB
/
iview-cli
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#!/usr/bin/env python3
import sys, os, argparse
import os.path
import iview.fetch
import iview.comm
from urllib.error import HTTPError
import iview.config
import configparser
from errno import EPIPE
from sys import stderr
import urllib.parse
from iview.utils import encodeerrors
def config():
try:
iview.comm.get_config()
except HTTPError as error:
print("""\
Error: could not retrieve an important configuration file from iView.
Please make sure you are connected to the Internet.
If iView works fine in your web browser, the iView API has possibly changed.
Try to find an updated version of this program or contact the author.
Debug: could not load URL {}""".format(error.url), file=stderr)
sys.exit(1)
def programme():
"""Downloads the iView index, and then prints each item in all series.
This is a slow function. Using index() and subsequently cherrypicking
a series to pass to series() is much faster.
"""
return category('index')
def category(keyword):
config()
index = iview.comm.get_keyword(keyword)
if not sys.stdout:
return
for series in index:
sys.stdout.write(encodeerrors(series['title'] + ':\n', sys.stdout))
print_series_items(series['items'], indent='\t')
def index():
"""Downloads the iView index, and prints the corresponding series and IDs.
"""
config()
index = iview.comm.get_index()
if not sys.stdout:
return
for series in index:
title = encodeerrors(series['title'], sys.stdout)
sys.stdout.write('{}\t{}\n'.format(series['id'], title))
def batch_index():
"""Downloads the iView index, and prints the corresponding series and IDs in a format
that works in the iview batch file
"""
config()
index = iview.comm.get_index()
if not sys.stdout:
return
for series in index:
title = encodeerrors(series['title'], sys.stdout)
sys.stdout.write('{}: {}\n'.format(series['id'], title))
def series(series_id):
config()
print_series_items(iview.comm.get_series_items(series_id))
def print_series_items(items, indent=''):
if not sys.stdout:
return
for item in items:
title = encodeerrors(item['title'], sys.stdout)
sys.stdout.write('{}{}\t({})\n'.format(indent, title, item['url']))
def print_auth():
config()
auth = iview.comm.get_auth()
print('iView auth data:')
for (desc, key) in (
('Streaming Host', 'host'),
('RTMP Token', 'token'),
('HDS Token', 'tokenhd'),
('Server URL', 'server'),
('Playpath Prefix', 'playpath_prefix'),
('Unmetered', 'free'),
):
value = auth.get(key)
if value is not None:
print('\t{}: {}'.format(desc, value))
def download(url, output=None):
config()
iview.fetch.fetch_program(url, execvp=True, dest_file=output)
def batch(batch_file):
config()
# parse the batch file
batch = configparser.ConfigParser()
batch.read(os.path.expanduser(batch_file))
items = batch.items('batch')
batch_destination = '.'
series_ids = []
last_only = False
# separate options from the series ids
for key, value in items:
if key == 'destination':
batch_destination = value
elif key == 'last_only':
if not(value == '0' or value.lower() == 'false' or value.lower() == "no"):
last_only = True
else:
# Note: currently the value after the series_id in the batch file
# is only used as a comment for the user.
series_ids.append(key)
# move to where the files should be downloaded
os.chdir(batch_destination)
# loop through the series ids
for series_id in series_ids:
# unset the last episode for the current series.
last_episode = None
# loop through the episodes
result = iview.comm.get_series_items(series_id, get_meta=True)
[episodes, metadata] = result
for episode in episodes:
if last_only:
# This last_only feature is experimental, I am not sure which field to
# use to determine the most recent episode.
if last_episode is None or episode['date'] > last_episode['date']:
last_episode = episode
else:
batch_fetch_program(episode, series=metadata['title'])
# Last only means we only get one episode for the series
if last_only:
batch_fetch_program(last_episode, series=metadata['title'])
def batch_fetch_program(episode, series):
# Only print notification messages for episodes that have never been downloaded before.
url = episode['url']
# urls sometimes include a path like 'news/' or 'kids/'
(pathpart, filepart) = os.path.split(url)
# urls also have '.mp4' extension but downloaded files end in '.flv'
(base, ext) = os.path.splitext(filepart)
title = episode['title']
filename = iview.fetch.descriptive_filename(series, title, url)
if os.path.isfile(base + '.flv') and not os.path.isfile(filename):
print("{} already exists as {}.flv so should be moved".format(filename, base))
return
if not os.path.isfile(filename):
msg = "getting {} - {} -> {}".format(episode['title'], episode['url'], filename)
print(msg, file=stderr)
iview.fetch.fetch_program(episode['url'], execvp=False, dest_file=filename, quiet=True)
def subtitles(name, output=None):
config()
url = name.rsplit('.', 1)[0]
if output is not None:
srt = output
else:
srt = url.rsplit('/', 1)[-1] + '.srt'
if not srt == '-' and os.path.isfile(srt):
print('Subtitles have already been downloaded to {}'.format(srt), file=stderr)
return False
msg = 'Downloading subtitles to {}...'.format(srt)
print(msg, end=' ', file=stderr)
try:
subtitles = iview.comm.get_captions(url)
except HTTPError as error:
print('failed', file=stderr)
print('Got an error when downloading {}'.format(error.url), file=stderr)
return False
if not srt == '-':
f = open(srt, 'wb')
else:
f = sys.stdout.detach()
sys.stdout = None
with f:
f.write(subtitles.encode('utf-8'))
print('done', file=stderr)
def parse_proxy_argument(proxy):
"""Try to parse 'proxy' as host:port pair. Returns an error message
if it cannot be understood. Otherwise, it configures the settings
in iview.config and returns None.
"""
try:
split = urllib.parse.SplitResult(scheme="", netloc=proxy,
path="", query="", fragment="")
if split.port is not None:
iview.config.socks_proxy_port = split.port
except ValueError as err:
return err
iview.config.socks_proxy_host = split.hostname
return None
def main():
params = argparse.ArgumentParser()
params.add_argument("-i", "--index", action="store_true",
help="print the iView index (number is the series ID)")
params.add_argument("-s", "--series", metavar="<id>",
help="get the list of programmes for the series")
params.add_argument("-k", "--category", metavar="<keyword>",
help="list programmes matching a category keyword")
params.add_argument("-p", "--programme", action="store_true",
help="""list all iView programmes at once""")
params.add_argument("-d", "--download", metavar="<url>",
help="""download a programme
(pass the url you got from -s, -k or -p)""")
params.add_argument("-t", "--subtitles" , metavar="<url>",
help="""download subtitles in SRT format for a programme
(pass the same url as for --download)""")
params.add_argument("-o", "--output", metavar="<file>",
help="specify a file to output to (use - for stdout)")
params.add_argument("--batch", metavar="<file>",
help="specify a batch operation file (for cronjob etc)")
params.add_argument("--bindex", action="store_true",
help="like --index but output is in batchfile format")
params.add_argument("-a", "--print-auth", action="store_true",
help="print debug iView auth information")
params.add_argument("-c", "--cache", metavar="<dir>",
help="use cache directory for debugging")
params.add_argument("--host", metavar="<name>",
help="override streaming host")
params.add_argument("--ip", metavar="<address>",
help="send IP address in auth request")
params.add_argument("-x", "--proxy", metavar="<host:port>",
help="use specified SOCKS proxy")
if len(sys.argv) <= 1:
params.print_help(stderr)
sys.exit(2)
args = params.parse_args()
if args.proxy is not None:
err = parse_proxy_argument(args.proxy)
if err is not None:
print("Invalid proxy specification: {}\n".format(err), file=stderr)
params.print_usage(stderr)
sys.exit(4)
iview.comm.configure_socks_proxy()
if args.cache is not None:
iview.config.cache = args.cache
if args.host is not None:
iview.config.override_host = args.host
if args.ip is not None:
iview.config.ip = args.ip
try:
if args.programme:
programme()
if args.category is not None:
category(args.category)
if args.index:
index()
if args.bindex:
batch_index()
if args.series is not None:
series(args.series)
if args.print_auth:
print_auth()
if args.download is not None:
download(args.download, args.output)
elif args.subtitles is not None:
subtitles(args.subtitles, args.output)
elif args.batch is not None:
batch(args.batch)
except iview.comm.Error as error:
print(error, file=stderr)
sys.exit(1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
sys.exit(1)
except EnvironmentError as err:
if err.errno == EPIPE:
sys.exit(1)
else:
raise