git ssb

0+

cel / dillo-ytdl



Tree: 498e3731c8cc2a4d9c000c5050c68465560ed39d

Files: 498e3731c8cc2a4d9c000c5050c68465560ed39d / ytdl.dpi

6346 bytesRaw
1#!/usr/bin/env python3
2
3# © 2018 cel @f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519
4# Copying and distribution of this file, with or without modification,
5# are permitted in any medium without royalty provided the copyright
6# notice and this notice are preserved. This file is offered as-is,
7# without any warranty.
8
9import socket
10import sys
11import re
12import threading
13import youtube_dl
14import urllib.parse
15import html
16import traceback
17import json
18
19authRe = re.compile("<cmd='auth' msg='([^']*)' '>")
20openRe = re.compile("<cmd='open_url' url='(.*?)' '>")
21byeRe = re.compile("<cmd='DpiBye' '>")
22
23print("[ytdl]: init", file=sys.stderr)
24
25def writeHeader(conn, url, contentType):
26 conn.send(("<cmd='start_send_page' url='" + url + "' '>").encode('utf-8'))
27 conn.send(('Content-type: ' + contentType + '\r\n\r\n').encode('utf-8'))
28
29def serveQuit(conn, url):
30 writeHeader(conn, url, "text/plain")
31 conn.send(b"quit")
32 conn.shutdown(socket.SHUT_WR)
33 print("[ytdl]: quit")
34 exit()
35
36def serveHome(conn, url, uri):
37 writeHeader(conn, url, "text/html")
38 conn.send(("<!doctype html>" +
39 "<html>" +
40 "<head>" +
41 "<title>ytdl</title></head>" +
42 "</head><body>\n" +
43 "<div>youtube_dl " + youtube_dl.version.__version__ + "</div>" +
44 "<div><a href=/quit>quit</a></div>" +
45 "</body></html>").encode('utf-8'))
46 conn.shutdown(socket.SHUT_WR)
47
48def serveEcho(conn, url, uri):
49 writeHeader(conn, url, "text/plain")
50 conn.send(str(uri).encode('utf-8'))
51 conn.shutdown(socket.SHUT_WR)
52
53def renderThumbnail(thumb):
54 url = thumb.get('url')
55 href = re.sub(r"default", "hqdefault", url)
56 return ("<a href='" + html.escape(href) + "'>" +
57 "<img src='" + html.escape(url) + "'" +
58 " alt='" + thumb.get('id', '') + "'></a>")
59
60def formatSize(size):
61 size = int(size)
62 if size < 1024: return size + ' B'
63 size /= 1024
64 if size < 1024: return '%.2f KB' % size
65 size /= 1024
66 if size < 1024: return '%.2f MB' % size
67 return '%.2f GB' % size
68
69def renderFormat(form):
70 id = form.get('format_id')
71 ext = form.get('ext')
72 name = form.get('format')
73 url = form.get('url')
74 resolution = form.get('resolution')
75 vcodec = form.get('vcodec')
76 acodec = form.get('acodec')
77 filesize = form.get('filesize')
78 return ("<tr><td>" +
79 ('<a href="' + html.escape(url) + '">' if url else "") +
80 html.escape(id) +
81 ('</a>' if url else "") +
82 "</td>" +
83 "<td>" + (html.escape(str(ext)) if ext else "") + "</td>" +
84 "<td align=right>" + (str(formatSize(filesize)) if filesize else "") + "</td>" +
85 "<td>" + (html.escape(str(name)) if name else "") + "</td>" +
86 "<td>" + (html.escape(str(resolution)) if resolution else "") + "</td>" +
87 "<td>" + (html.escape(str(acodec)) if acodec else "") + "</td>" +
88 "<td>" + (html.escape(str(vcodec)) if vcodec else "") + "</td>" +
89 "<!--<td><pre>" + json.dumps(form) + "</pre></td>-->")
90
91def renderEntry(entry):
92 thumbnails = entry.get('thumbnails', [])
93 formats = entry.get('formats', [])
94 uploader = entry.get('uploader')
95 description = entry.get('description')
96 duration = entry.get('duration')
97 view_count = entry.get('view_count')
98
99 if formats: del entry['formats']
100 tags = entry.get('tags')
101 return (
102 ("<div>" +
103 " ".join([renderThumbnail(thumb) + "\n" for thumb in thumbnails]) +
104 "</div>\n" if len(thumbnails) > 0 else "") +
105 ("<div>" + html.escape(uploader) + "</div>" if uploader else "") +
106 ("<p>" + html.escape(description) + "</p>" if description else "") +
107 ("<p>Duration: " + html.escape(str(duration)) + "</p>" if duration else "") +
108 ("<p>View count: " + html.escape(str(view_count)) + "</p>" if view_count else "") +
109 ("<p>Tags: " + ", ".join([html.escape(str(tag)) for tag in tags]) + "</p>" if tags else "") +
110 ("<table>" +
111 "<thead>" +
112 "<tr><th>code</th><th>ext</th><th>size</th><th>format</th><th>resolution</th><th>audio</th><th>video</th></tr>" +
113 "</thead>\n<tbody>" +
114 "\n".join([renderFormat(form) + "\n" for form in formats]) +
115 "</tbody></table>\n" if len(thumbnails) > 0 else ""))
116
117def serveResult(conn, origUrl, res):
118 writeHeader(conn, origUrl, "text/html")
119 title = res.get('title')
120 webpage_url = res.get('webpage_url')
121 entries = res.get('entries', []) if res.get('_type', None) == 'playlist' else [res]
122 conn.send(("<!doctype html>" +
123 "<html>" +
124 "<head>" +
125 "<title>" + html.escape(title) + "</title></head>" +
126 "</head><body>\n" +
127 "<h1>" +
128 ("<a href='" + html.escape(webpage_url) + "'>" if webpage_url else "") +
129 html.escape(title) +
130 ("</a>" if webpage_url else "") +
131 "</h1>\n" +
132 "\n".join([renderEntry(entry) for entry in entries]) +
133 "<!--<pre>" + json.dumps(res, indent=True) + "</pre>-->\n" +
134 "</body></html>").encode('utf-8'))
135 conn.shutdown(socket.SHUT_WR)
136
137def serveOpen(conn, url):
138 uri = urllib.parse.urlparse(url)
139 if uri.scheme == "dpi": return serveEcho(conn, url, uri)
140 if uri.netloc == "" and (uri.path == "" or uri.path == "/"):
141 return serveHome(conn, url, uri)
142 if uri.scheme == "ytdl" or uri.scheme == "ydl":
143 if '.' in uri.netloc:
144 return serveResource(conn, url, urllib.parse.urlunparse((
145 'https',
146 uri.netloc,
147 uri.path,
148 uri.params,
149 uri.query,
150 uri.fragment
151 )))
152 return serveResource(conn, url, re.sub('yt?dl:(//)?', '', url))
153 return serveEcho(conn, url, uri)
154
155def serveResource(conn, origUrl, url):
156 try:
157 res = ytdl.extract_info(url, download=False)
158 except:
159 etype, value, tb = sys.exc_info()
160 writeHeader(conn, origUrl, "text/plain")
161 lines = traceback.format_exception(etype, value, tb)
162 conn.send((''.join(lines)).encode('utf-8'))
163 conn.shutdown(socket.SHUT_WR)
164 else:
165 serveResult(conn, origUrl, res)
166
167def handleConn(conn, addr):
168 buf = ""
169 while True:
170 data = conn.recv(1024)
171 if not data: break
172 buf += data.decode('utf-8')
173
174 while len(buf) > 0:
175 if buf[0] != '<':
176 conn.close()
177 return
178
179 m = authRe.match(buf)
180 if m:
181 buf = buf[m.end():]
182 continue
183
184 m = openRe.match(buf)
185 if m:
186 buf = buf[m.end():]
187 url = m.group(1)
188 if url == "ytdl:/quit": return serveQuit(conn, url)
189 threading.Thread(target=serveOpen, args=(conn, url)).start()
190 return
191
192 m = byeRe.match(buf)
193 if m:
194 print("[ytdl]: bye")
195 exit()
196
197stdin = socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM)
198
199ytdl = youtube_dl.YoutubeDL({
200 'no_color': True
201})
202
203while True:
204 try:
205 conn, addr = stdin.accept()
206 except KeyboardInterrupt:
207 exit()
208 else:
209 handleConn(conn, addr)
210

Built with git-ssb-web