SHOW:
|
|
- or go back to the newest paste.
1 | - | #!/usr/local/bin/python3 |
1 | + | #!/usr/bin/env python3 |
2 | """ | |
3 | A simple little script for getting at the m3u8 for ufc.tv videos | |
4 | For free and ethical stream viewing free from proprietary players | |
5 | ||
6 | Doesn't circumvent geo-blocks or login requirements | |
7 | You can get around the ufc.tv geo blocks via DNS proxies | |
8 | ||
9 | I have this saved as ufctv in a bin dir on my $PATH, so I can call it from wherever | |
10 | ||
11 | To login, run: ufctv login | |
12 | That will begin an interactive login prompt | |
13 | The login details themselves aren't saved, just the cookies for the session | |
14 | And the active session is reset any time you login with the "Keep Me Signed In" box anywhere else | |
15 | ||
16 | To get a video's m3u8: ufctv m3u8 $UFCTV-URL | |
17 | So for example if I want to go back and watch Belfort vs Hendo 3 | |
18 | % ufctv m3u8 http://www.ufc.tv/video/belfort-vs-henderson-3 | |
19 | From there you an do what you wish with the m3u8! | |
20 | ||
21 | You may wish to pipe the result direct to mpv (which lets you seek around and switch between qualities) | |
22 | % ufctv m3u8 http://www.ufc.tv/video/belfort-vs-henderson-3 | xargs mpv | |
23 | ||
24 | Or start ripping with livestreamer | |
25 | % ufctv m3u8 http://www.ufc.tv/video/belfort-vs-henderson-3 | xargs -I M3U8 livestreamer hlsvariant://M3U8 best -o belf-vs-hend.ts | |
26 | You can also rip the stream with ffmpeg, though I've found livestreamer does it a bit cleaner | |
27 | ||
28 | You may also wish to expand the program to add commands to do this for you instead of via shell piping | |
29 | """ | |
30 | ||
31 | import argparse | |
32 | import getpass | |
33 | import json | |
34 | import os | |
35 | import pickle | |
36 | import re | |
37 | import subprocess | |
38 | import sys | |
39 | ||
40 | from requests import session | |
41 | ||
42 | def main(): | |
43 | parser = argparse.ArgumentParser(description= | |
44 | """ | |
45 | This is a command line tool to help enjoy quality UFC Fight Pass | |
46 | content in an ethical manner that respects your freedoms | |
47 | """) | |
48 | ||
49 | parser.add_argument('operation', | |
50 | choices=['login', 'm3u8']) | |
51 | ||
52 | parser.add_argument('rest', nargs='*') | |
53 | ||
54 | args = parser.parse_args() | |
55 | ||
56 | op = args.operation | |
57 | if op == 'login': | |
58 | return ufctv_login() | |
59 | ||
60 | if len(args.rest) < 1: | |
61 | print("Missing argument: video_url", file=sys.stderr) | |
62 | return 1 | |
63 | ||
64 | vid_url = args.rest[0] | |
65 | uri = get_m3u8(vid_url) | |
66 | if uri is None: | |
67 | return 2 | |
68 | ||
69 | if args.operation == 'm3u8': | |
70 | print(uri) | |
71 | return 0 | |
72 | ||
73 | def fake_mobile_session(): | |
74 | with session() as c: | |
75 | load_cookies(c) | |
76 | c.headers.update({'User-Agent': ( | |
77 | "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)" | |
78 | " AppleWebKit/528.18 (KHTML, like Gecko)" | |
79 | " Version/4.0 Mobile/7A341 Safari/528.16")}) | |
80 | return c | |
81 | ||
82 | def get_m3u8_by_id(session, vid_id): | |
83 | puburi = "http://www.ufc.tv/service/publishpoint?type=video&id={}&format=json" | |
84 | r = session.get(puburi.format(vid_id)) | |
85 | stream_info = json.loads(r.text) | |
86 | if 'path' not in stream_info: | |
87 | return None | |
88 | return stream_info['path'].replace("_iphone", "") | |
89 | ||
90 | def get_m3u8(uri): | |
91 | with fake_mobile_session() as c: | |
92 | r = c.get(uri) | |
93 | ||
94 | vid_id = find_video_id(r.text) | |
95 | if vid_id is None: | |
96 | print("Failed to find video ID in page.", file=sys.stderr) | |
97 | return None | |
98 | ||
99 | if not logged_in(r.text): | |
100 | print("Goof Alert: You are not logged in to UFC.TV", file=sys.stderr) | |
101 | ||
102 | if not video_allowed(r.text): | |
103 | print("Video is not available", file=sys.stderr) | |
104 | return None | |
105 | ||
106 | res = get_m3u8_by_id(c, vid_id) | |
107 | save_cookies(c) | |
108 | return res | |
109 | ||
110 | ufctv_sesssion_path = os.path.expanduser("~/.ufctv") | |
111 | ||
112 | def save_cookies(session): | |
113 | with open(ufctv_sesssion_path, 'wb') as f: | |
114 | pickle.dump(session.cookies, f) | |
115 | ||
116 | def load_cookies(session): | |
117 | if not os.path.isfile(ufctv_sesssion_path): | |
118 | print("No saved cookies file found -- starting from scratch", file=sys.stderr) | |
119 | return | |
120 | ||
121 | with open(ufctv_sesssion_path, 'rb') as f: | |
122 | session.cookies.update(pickle.load(f)) | |
123 | ||
124 | def video_allowed(page_html): | |
125 | return '<div class="noAccess">' not in page_html | |
126 | ||
127 | def logged_in(page_html): | |
128 | return "Sign Out" in page_html | |
129 | ||
130 | def ufctv_login(): | |
131 | with fake_mobile_session() as c: | |
132 | login(c) | |
133 | save_cookies(c) | |
134 | ||
135 | def login(sesh): | |
136 | username = input("Please enter your ufc.tv username: ") | |
137 | password = getpass.getpass("Please enter your ufc.tv password: ") | |
138 | longchoice = input("Tick the 'Keep Me Signed In' box for a long lasting session? [y/n] ") | |
139 | longsesh = 'true' if longchoice is 'y' else 'false' | |
140 | ||
141 | login = { | |
142 | 'username': username, | |
143 | 'password': password, | |
144 | 'cookielink': longsesh | |
145 | } | |
146 | ||
147 | r = sesh.post("https://www.ufc.tv/secure/authenticate", data=login) | |
148 | ||
149 | if 'loginsuccess' not in r.text: | |
150 | print("Login failure -- full response:\n{}\n".format(r.text), file=sys.stderr) | |
151 | return False | |
152 | ||
153 | return True | |
154 | ||
155 | def find_video_id(page_html): | |
156 | m = re.search("rel=\"image_src\" href=\".*?([0-9]+?)_.*?\.(jpg|png)\"", page_html) | |
157 | if m is None: | |
158 | return m | |
159 | return m.group(1) | |
160 | ||
161 | if __name__ == "__main__": | |
162 | sys.exit(main()) |