@@ -24,6 +24,8 @@ def canonicalize(x):
24
24
[default: read from stdin]""" )
25
25
parser .add_argument ("-o" , "--outdir" , metavar = "DIR" ,
26
26
help = "output directory [default: 'slides' subdirectory of the script's directory]" )
27
+ parser .add_argument ("-H" , "--html" , action = 'store_true' ,
28
+ help = "export HTML preview instead of PNG" )
27
29
parser .add_argument ("-c" , "--clean" , action = 'store_true' ,
28
30
help = "delete output directory before downloading (DANGEROUS!)" )
29
31
parser .add_argument ("-n" , "--dry-run" , action = 'store_true' ,
@@ -64,11 +66,13 @@ def canonicalize(x):
64
66
65
67
# our super-simplistic, very special-cased parser
66
68
html = html .split ("<tbody" , 1 )[- 1 ]
69
+ docs = {}
67
70
for attrs , tr in re .findall (r'<tr([^>]*)>(.*?)</tr>' , html , flags = re .I + re .S ):
68
71
row = [re .sub (r'<[^>]+>' , '' , td ).strip ()
69
72
for attrs , td
70
73
in re .findall (r'<td([^>]*)>(.*?)</td>' , tr , flags = re .I + re .S )]
71
- url = re .search (r'href="(https?://.*?\.(png|jpe?g))"' , tr , flags = re .I )
74
+ re_ext = "html?" if args .html else "png|jpe?g"
75
+ url = re .search (r'href="(https?://.*?\.(' + re_ext + '))"' , tr , flags = re .I )
72
76
if url : url = url .group (1 )
73
77
if not (url ) or (len (row ) < 5 ):
74
78
print (f"WARNING: invalid slide { row [:5 ]} " , file = sys .stderr )
@@ -86,32 +90,175 @@ def canonicalize(x):
86
90
+ "_" + pg_slide .group (2 )
87
91
else :
88
92
digits_at_end = re .search (r'(\d+)$' , name )
89
- if ("coming" in stype ) and (("coming" in name ) or ("now" in name )):
93
+ if (( "coming" in stype ) or ( "now" in stype ) ) and (("coming" in name ) or ("now" in name )):
90
94
name = "00_" + name
91
- if digits_at_end and ("competition" in stype ):
95
+ if digits_at_end and (( "competition" in stype ) or ( "compo_entry" in stype ) ):
92
96
name = digits_at_end .group (1 ).rjust (2 , '0' )
93
97
if ("end" in name ) and ("end" in stype ):
94
98
name = "99_end"
95
99
96
- # download
97
- outdir = os .path .join (basedir , folder )
98
- if not (os .path .isdir (outdir )) and not (args .dry_run ):
100
+ # download PNG
101
+ if not args .html :
102
+ outdir = os .path .join (basedir , folder )
103
+ if not (os .path .isdir (outdir )) and not (args .dry_run ):
104
+ try :
105
+ os .makedirs (outdir )
106
+ except EnvironmentError as e :
107
+ print (f"ERROR: can't create directory '{ outdir } ':" , e , file = sys .stderr )
108
+ continue
109
+ outfile = os .path .join (outdir , name + ext )
110
+ if args .verbose :
111
+ print (url )
112
+ if args .dry_run :
113
+ print ("=>" , outfile )
114
+ else :
115
+ print ("=>" , outfile , end = ' ' )
116
+ sys .stdout .flush ()
117
+ try :
118
+ with urllib .request .urlopen (url , context = ssl_ctx ) as f_in , open (outfile , 'wb' ) as f_out :
119
+ f_out .write (f_in .read ())
120
+ print ("[OK]" )
121
+ except EnvironmentError as e :
122
+ print (e )
123
+
124
+ # handle HTML
125
+ if args .html :
126
+ if not folder in docs :
127
+ docs [folder ] = {}
128
+ if args .dry_run :
129
+ print (f"{ folder } /{ name } <= { url } " )
130
+ else :
131
+ print (f"<= { folder } /{ name } " , end = ' ' )
132
+ try :
133
+ with urllib .request .urlopen (url , context = ssl_ctx ) as f_in :
134
+ docs [folder ][name ] = f_in .read ().decode ('utf-8' , 'replace' )
135
+ print ("[OK]" )
136
+ except EnvironmentError as e :
137
+ print (e )
138
+
139
+ # create HTML output
140
+ if args .html and not (args .dry_run ):
141
+ if not os .path .isdir (basedir ):
99
142
try :
100
- os .makedirs (outdir )
143
+ os .makedirs (basedir )
101
144
except EnvironmentError as e :
102
- print (f"ERROR: can't create directory '{ outdir } ':" , e , file = sys .stderr )
103
- continue
104
- outfile = os .path .join (outdir , name + ext )
105
- if args .verbose :
106
- print (url )
107
- if args .dry_run :
108
- print ("=>" , outfile )
109
- else :
145
+ print (f"ERROR: can't create directory '{ basedir } ':" , e , file = sys .stderr )
146
+ sys .exit (1 )
147
+
148
+ domain = url [:10 ] + url [10 :].split ('/' , 1 )[0 ] + '/'
149
+
150
+ for compo , cdata in docs .items ():
151
+ outfile = os .path .join (basedir , compo + ".html" )
110
152
print ("=>" , outfile , end = ' ' )
111
- sys .stdout .flush ()
112
- try :
113
- with urllib .request .urlopen (url , context = ssl_ctx ) as f_in , open (outfile , 'wb' ) as f_out :
114
- f_out .write (f_in .read ())
153
+
154
+ doc = list (cdata .values ())[0 ]
155
+ header , doc = doc .split ('<div id="slidemeister">' , 1 )
156
+ footer = '<script>' + doc .split ('<script>' , 1 )[- 1 ]
157
+
158
+ header = header .replace ('href="/' , 'href="' + domain )
159
+
160
+ doc = header .replace ('</style>' , """
161
+ .exported_off { display:none !important; }
162
+ </style>""" )
163
+
164
+ if os .path .exists (os .path .join (basedir , "patch.js" )):
165
+ doc += """
166
+ <canvas id="glcanvas" width="100vw" height="100vh" tabindex="1"></canvas>
167
+ <script type="text/javascript" src="patch.js" async></script>
168
+ <script>
169
+ function showError(errId, errMsg)
170
+ {
171
+ console.log("Cables error", errId, ":", errMsg);
172
+ }
173
+ document.addEventListener("CABLES.jsLoaded", function (event)
174
+ {
175
+ CABLES.patch = new CABLES.Patch({
176
+ patch: CABLES.exportedPatch,
177
+ "prefixAssetPath": "",
178
+ "assetPath": "assets/",
179
+ "jsPath": "",
180
+ "glCanvasId": "glcanvas",
181
+ "glCanvasResizeToWindow": true,
182
+ "onError": showError,
183
+ "canvas": {"alpha":true, "premultipliedAlpha":true } // make canvas transparent
184
+ });
185
+ });
186
+ </script>
187
+ """
188
+
189
+ div_attrs = 'class="exported_slide" id="slidemeister"'
190
+ for name , subdoc in sorted (cdata .items ()):
191
+ subdoc = subdoc .split ('<div id="slidemeister">' , 1 )[- 1 ]
192
+ if name .isdigit ():
193
+ stype = "compo"
194
+ else :
195
+ stype = '' .join (c for c in name if c .isalpha ())
196
+ div_attrs += f' data-slide-type="{ stype } "'
197
+ doc += f"<div { div_attrs } >" + subdoc .split ('<script>' , 1 )[0 ]
198
+ div_attrs = 'class="exported_slide exported_off"'
199
+
200
+ doc += footer .replace ('</script>' , """
201
+ var transitionFrom = null;
202
+ var transitionTo = null;
203
+ const transitionDuration = 1.0; // seconds
204
+ const opacityStep = 2.0 / (60 * transitionDuration);
205
+ var fadeOpacity = null;
206
+
207
+ function fadeInHandler() {
208
+ fadeOpacity += opacityStep;
209
+ if (fadeOpacity >= 1.0) { fadeOpacity = 1.0; }
210
+ transitionTo.style.opacity = fadeOpacity;
211
+ if (fadeOpacity >= 1.0) {
212
+ console.log("transition finished");
213
+ } else {
214
+ window.requestAnimationFrame(fadeInHandler);
215
+ }
216
+ }
217
+
218
+ function fadeOutHandler() {
219
+ fadeOpacity -= opacityStep;
220
+ if (fadeOpacity <= 0.0) { fadeOpacity = 0.0; }
221
+ transitionFrom.style.opacity = fadeOpacity;
222
+ if (fadeOpacity <= 0.0) {
223
+ transitionFrom.classList.add("exported_off");
224
+ transitionFrom.id = "noid";
225
+ transitionTo.style.opacity = 0.0;
226
+ transitionTo.id = "slidemeister";
227
+ transitionTo.classList.remove("exported_off");
228
+ CABLES.patch.setVariable("SLIDETYPE", transitionTo.dataset.slideType);
229
+ window.requestAnimationFrame(fadeInHandler);
230
+ } else {
231
+ window.requestAnimationFrame(fadeOutHandler);
232
+ }
233
+ }
234
+
235
+ function startTransition(from, to) {
236
+ transitionFrom = from;
237
+ transitionTo = to;
238
+ if (!from || !to) {
239
+ console.log("no transition possible");
240
+ return;
241
+ }
242
+ transitionFrom.style.opacity = fadeOpacity = 1.0;
243
+ window.requestAnimationFrame(fadeOutHandler);
244
+ }
245
+
246
+ window.addEventListener('keydown', (event) => {
247
+ var prevSlide = null;
248
+ var currentSlide = null;
249
+ var nextSlide = null;
250
+ const slides = document.querySelectorAll(".exported_slide");
251
+ for (var i = 0; i < slides.length; ++i) {
252
+ const slide = slides[i];
253
+ if (currentSlide && !nextSlide) { nextSlide = slide; }
254
+ if (slide.id == "slidemeister") { currentSlide = slide; }
255
+ if (!currentSlide) { prevSlide = slide; }
256
+ }
257
+ if (event.code == "ArrowLeft") startTransition(currentSlide, prevSlide);
258
+ if (event.code == "ArrowRight") startTransition(currentSlide, nextSlide);
259
+ });
260
+ </script>""" )
261
+
262
+ with open (outfile , 'w' , encoding = 'utf-8' ) as f :
263
+ f .write (doc )
115
264
print ("[OK]" )
116
- except EnvironmentError as e :
117
- print (e )
0 commit comments