git ssb

0+

cel / dillo-ytdl



Commit 58a3d11c862df8cdb00f077e91d0303ba78618a3

Init

cel committed on 1/10/2019, 8:47:26 PM

Files changed

README.mdadded
requirements.txtadded
ydl.dpiadded
README.mdView
@@ -1,0 +1,30 @@
1 +# dillo-ydl
2 +
3 +[youtube-dl][] plugin for [Dillo][].
4 +
5 +It lets you visit URLs of the form "ydl:<url>" where `<url>` is the URL or video ID you would pass to `youtube-dl`, and you will get a page showing some info about the video, including a table of formats (as you would get from `youtube-dl -F`) with a link to download each one.
6 +
7 +## Install
8 +
9 +```sh
10 +git clone ssb://%oEzgqM849P03lR3tR7qwbjzw/c+R+v0oIoHyDgEDrbw=.sha256 dillo-ydl
11 +cd dillo-ydl
12 +pip install -r requirements.txt # or sudo apt-get install youtube-dl
13 +mkdir -p ~/.dillo/dpi/ydl
14 +cp ydl.dpi ~/.dillo/dpi/ydl/ # or ln -rs ydl.dpi ~/.dillo/dpi/ydl/
15 +test -f ~/.dillo/dpidrc || cp /etc/dillo/dpidrc ~/.dillo/dpidrc
16 +echo 'proto.ydl=ydl/ydl.dpi' >> ~/.dillo/dpidrc
17 +dpidc stop
18 +```
19 +
20 +[youtube-dl]: https://rg3.github.io/youtube-dl/
21 +[Dillo]: https://www.dillo.org/
22 +
23 +## License
24 +
25 +© 2018 cel @f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519
26 +
27 +Copying and distribution of this file, with or without modification,
28 +are permitted in any medium without royalty provided the copyright
29 +notice and this notice are preserved. This file is offered as-is,
30 +without any warranty.
requirements.txtView
@@ -1,0 +1,1 @@
1 +youtube-dl
ydl.dpiView
@@ -1,0 +1,185 @@
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 +
9 +import socket
10 +import sys
11 +import re
12 +import threading
13 +import youtube_dl
14 +import urllib.parse
15 +import html
16 +import traceback
17 +import json
18 +
19 +authRe = re.compile("<cmd='auth' msg='([^']*)' '>")
20 +openRe = re.compile("<cmd='open_url' url='(.*?)' '>")
21 +byeRe = re.compile("<cmd='DpiBye' '>")
22 +
23 +print("[ydl]: init", file=sys.stderr)
24 +
25 +def 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 +
29 +def serveQuit(conn, url):
30 + writeHeader(conn, url, "text/plain")
31 + conn.send(b"quit")
32 + conn.shutdown(socket.SHUT_WR)
33 + print("[ydl]: quit")
34 + exit()
35 +
36 +def serveHome(conn, url, uri):
37 + writeHeader(conn, url, "text/html")
38 + conn.send(("<!doctype html>" +
39 + "<html>" +
40 + "<head>" +
41 + "<title>ydl</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 +
48 +def 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 +
53 +def 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 +
60 +def 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 +
69 +def 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 +
91 +def serveResult(conn, origUrl, res):
92 + writeHeader(conn, origUrl, "text/html")
93 + title = res.get('title')
94 + webpage_url = res.get('webpage_url')
95 + thumbnails = res.get('thumbnails', [])
96 + formats = res.get('formats', [])
97 + uploader = res.get('uploader')
98 + description = res.get('description')
99 + if formats: del res['formats']
100 + conn.send(("<!doctype html>" +
101 + "<html>" +
102 + "<head>" +
103 + "<title>" + html.escape(title) + "</title></head>" +
104 + "</head><body>\n" +
105 + "<h1>" +
106 + ("<a href='" + html.escape(webpage_url) + "'>" if webpage_url else "") +
107 + html.escape(title) +
108 + ("</a>" if webpage_url else "") +
109 + "</h1>\n" +
110 + ("<div>" +
111 + " ".join([renderThumbnail(thumb) + "\n" for thumb in thumbnails]) +
112 + "</div>\n" if len(thumbnails) > 0 else "") +
113 + ("<div>" + html.escape(uploader) + "</div>" if uploader else "") +
114 + ("<p>" + html.escape(description) + "</p>" if description else "") +
115 + ("<table>" +
116 + "<thead>" +
117 + "<tr><th>code</th><th>ext</th><th>size</th><th>format</th><th>resolution</th><th>audio</th><th>video</th></tr>" +
118 + "</thead>\n<tbody>" +
119 + "\n".join([renderFormat(form) + "\n" for form in formats]) +
120 + "</tbody></table>\n" if len(thumbnails) > 0 else "") +
121 + "<!--<pre>" + json.dumps(res, indent=True) + "</pre>-->\n" +
122 + "</body></html>").encode('utf-8'))
123 + conn.shutdown(socket.SHUT_WR)
124 +
125 +def serveOpen(conn, url):
126 + uri = urllib.parse.urlparse(url)
127 + if uri.path == "" or uri.path == "/": return serveHome(conn, url, uri)
128 + if uri.scheme == "dpi": return serveEcho(conn, url, uri)
129 + serveResource(conn, url, url[4:])
130 +
131 +def serveResource(conn, origUrl, url):
132 + try:
133 + res = ydl.extract_info(url, download=False)
134 + except:
135 + etype, value, tb = sys.exc_info()
136 + writeHeader(conn, origUrl, "text/plain")
137 + lines = traceback.format_exception(etype, value, tb)
138 + conn.send((''.join(lines)).encode('utf-8'))
139 + conn.shutdown(socket.SHUT_WR)
140 + else:
141 + serveResult(conn, origUrl, res)
142 +
143 +def handleConn(conn, addr):
144 + buf = ""
145 + while True:
146 + data = conn.recv(1024)
147 + if not data: break
148 + buf += data.decode('utf-8')
149 +
150 + while len(buf) > 0:
151 + if buf[0] != '<':
152 + conn.close()
153 + return
154 +
155 + m = authRe.match(buf)
156 + if m:
157 + buf = buf[m.end():]
158 + continue
159 +
160 + m = openRe.match(buf)
161 + if m:
162 + buf = buf[m.end():]
163 + url = m.group(1)
164 + if url == "ydl:/quit": return serveQuit(conn, url)
165 + threading.Thread(target=serveOpen, args=(conn, url)).start()
166 + return
167 +
168 + m = byeRe.match(buf)
169 + if m:
170 + print("[ydl]: bye")
171 + exit()
172 +
173 +stdin = socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM)
174 +
175 +ydl = youtube_dl.YoutubeDL({
176 + 'no_color': True
177 +})
178 +
179 +while True:
180 + try:
181 + conn, addr = stdin.accept()
182 + except KeyboardInterrupt:
183 + exit()
184 + else:
185 + handleConn(conn, addr)

Built with git-ssb-web