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()) |