]> git.gir.st - ttxd.git/blob - index.cgi
vt100 mode: teletext in the terminal
[ttxd.git] / index.cgi
1 #!/usr/bin/perl -X
2 # vim: foldmethod=marker
3
4 use strict;
5 use warnings;
6 use 5.010;
7 use utf8;use open qw/:std :utf8/;
8
9 use ORFText;
10
11 =pod
12 TODO: %headings won't register when topstory is missing (->sportarten)
13 this is version 2.0 of ttxd
14 values for query string/cookie:
15 - special flags start with _
16 - _noqna: hide "fragen/antworten" (set by default)
17 - numeric: pagenumbers
18 =cut
19
20 my $pages = $ENV{HTTP_COOKIE} // join '&', (
21 101, 113 .. 125, # Politik: Österreich/EU
22 127 .. 134, # Politik: International
23 102,#136 .. 144, # Chronik
24 # 701 .. 709 # Bundesländer (W-NÖ-B-OÖ-S-T-V-K-St)
25 # 471 .. 478 # Nachrichten leicht verständlich
26 # 103, 146 .. 149 # Leute
27 # 104, 151 .. 159 # Wirtschaft
28 # 161,167,169, # Börse
29 # 170,172,174 # -"-
30 # 105, 203 .. 209, # Sport vom Tag
31 # 210 .. 259, # Sportarten
32 # 261 .. 278, # Wintersport & Großereignisse
33 # 107,110,191..198, # Show und Kultur
34 108,#461 .. 465, # Web/Media
35 # 468 .. 469, # Webtipps
36 # Special Flags:
37 "_noqna", # hide Q-and-A
38 );
39
40 my @pages = split /&/, ($ENV{QUERY_STRING} =~ s/(\d{3})-(\d{3})/${\join'&',$1..$2}/gr) // $pages;
41
42 # pages from query string won't affect cookie
43 #print "Set-Cookie: $pages; Max-Age=2147483647\r\n";
44 print "Content-type: text/html\r\n\r\n";
45
46 # html format {{{
47 $ORFText::REF_MARKUP = sub {
48 return qq{<a href="?$_[1]" data-page="$_[1]" class="ttxref" target="_blank">$_[0]<b>$_[1]</b></a>}
49 };
50 sub format_table {
51 my $text = join("\n",map{s/[\x00-\x20]/ /gr}@{%{$_[0]}{raw}})=~s/^ ,,+//msr=~s/\n+$//r;
52 return {%$_, text=>"<pre>$text</pre>"};
53 }
54 sub format_html { my %page = %{$_[0] or return};
55 no warnings 'numeric';
56 use List::Util qw[max];
57 sub ifdef { $_[1]? "$_[0]$_[1]":"" }
58 my $pagespec = substr($page{page},0,1) ."00/$page{page}_000".max($page{subpage},1);
59
60 return <<"HTML";
61 <section class=page>
62 <h3 title='@{[join' - ', $page{ressort}||(), $page{subressort}||(), $page{topic}||()]}'>
63 <a id="page-$page{page}">$page{page}${\ifdef('.',$page{subpage})}:</a>
64 $page{title}
65 </h3>
66 <p>$page{text}
67 <a title="Permanentlink zum Teilen erstellen" href="https://web.archive.org/save/http://text.orf.at/$pagespec.htm" class="archive" target="_blank">&#x27a4;</a>
68 <p class=advert title="Werbung">${\($page{advert}=~s|\b(\d{3})\b|<a href="?$1" target="_blank">$1</a>|gr)}</p>
69 </section>
70 HTML
71 }
72 # }}}
73 # headings {{{
74 my %headings = (
75 101 => "Politik: Österreich/EU",
76 127 => "Politik: International",
77 102 => "Chronik",
78 103 => "Leute",
79 104 => "Wirtschaft",
80 161 => "Börse",
81 105 => "Sport",
82 #203 => "Sport vom Tag",
83 #260 => "Wintersport und Großereignisse"
84 106 => "Fernsehen",
85 108 => "Multimedia",
86 107 => "Kultur &amp; Show", # XXX: this will show it multiple times!
87 110 => "Kultur &amp; Show",
88 109 => "Wetter",
89 471 => "Nachrichten leicht verständlich",
90 481 => "Nachrichten leichter verständlich",
91 701 => "Wien",
92 702 => "Niederösterreich",
93 703 => "Burgenland",
94 704 => "Oberösterreich",
95 705 => "Salzburg",
96 706 => "Tirol",
97 707 => "Vorarlberg",
98 708 => "Kärnten",
99 709 => "Steiermark",
100
101 );
102 # }}}
103 # html-head {{{
104 print <<'EOF';
105 <!DOCTYPE html>
106 <html lang='de'>
107 <meta charset='utf-8'>
108 <meta name='viewport' content='width=device-width, initial-scale=1'>
109 <title>ORFText News</title>
110 <style>
111 body {
112 text-align:justify;
113 hyphens:auto;
114
115 color:#ddd;
116 background:black;
117 max-width:30em;
118 margin:auto;
119 padding:.5em;
120 }
121 h1{font-family: sans-serif}
122 h1, b { color:white; }
123 body h2 { font-size: larger; }
124 a:link, a:visited { color: white; }
125 a:hover, a:active { color: #eeeeee; }
126
127 article {
128 font:large serif;
129 line-height:1.5;
130 }
131
132 section.page {
133 margin-bottom: 2em;
134 }
135 section.page p {
136 margin: 0;
137 }
138 section.page h3 {
139 font-size: 1.1em;
140 color: white;
141 margin: 0;
142 }
143 section.page h3 a{
144 font-weight: normal;
145 color: #ddd;
146 }
147 section.page h4 {
148 font-size: 1em;
149 margin: 0;
150 }
151 a.archive {
152 text-decoration:none;
153 font-size:xx-small;
154 vertical-align:middle;
155 }
156 a.config {
157 float:right;
158 }
159 h1 a {
160 text-decoration:none;
161 }
162 .page p.advert a {
163 color: black;
164 }
165 .page p.advert {
166 text-align: left;
167 margin-top: .5em;
168 color: #333;
169 background: lightgrey;
170 }
171 p.errors {
172 line-height: 1;
173 }
174 pre { /*sporttablellen*/
175 margin: 0;
176 max-width: 100%;
177 overflow-x: auto;
178 /*experimental:*/
179 display: inline-block;
180 vertical-align: text-bottom;
181 }
182 </style>
183 <script>
184 document.addEventListener("DOMContentLoaded", function(event) {
185 document.querySelectorAll("a.ttxref").forEach(e=>{
186 var n = e.getAttribute("data-page");
187 if(document.querySelector("#page-"+n) !== null) {
188 e.href="#page-"+n;
189 e.target="_self";
190 }
191 });
192 });
193 </script>
194
195 <article>
196 <h1><a href="?" title="zur Startseite">ORFText News</a></h1>
197 EOF
198 # }}}
199 my (@all, @nonempty);
200 page:foreach (grep/[0-9]/,@pages) {
201 print "<h2>$headings{$_}</h2>\n" if $headings{$_};
202
203 my $pageno = $_;
204 push @all, $pageno;
205 foreach (glob "/run/ttxd/spool/2/${_}_*.vtx") {
206 my $file_age = time - (stat)[9];
207 next if $file_age > 300;
208
209 for (ORFText::html($_)) {
210 next unless $_; # fail req'd for $nonempty
211 if ("_noqna" ~~ @pages) { # hide Q-and-A
212 next page if %{$_}{text}=~m@[Aa]lle Fra.?gen/Antworten.+?500@;# /.?/ is a soft hyphen
213 }
214 $_ = format_table $_ if %$_{tabular};
215 print format_html $_ ;
216 push @nonempty, $pageno;
217 }
218 }
219 }
220
221 sub subtr { return grep{not%{{map{$_=>1}@{$_[1]}}}{$_}} @{$_[0]}; }
222 sub uniq { return sort keys %{{@_}}; }
223 my @empty = uniq subtr [sort @all], [@nonempty]; # WARN: uniq breaks when sort is removed!
224 my ($N, $n) = ('n'x!!$#empty, 'n'x!!$#all);
225 print "<p class=errors>";
226 print "<small>Tafel$N @{[join ', ', @empty]} wurde$N nicht gefunden. </small>" if @empty;
227 print "</p>";
228
229 print "<section class=links>";
230 print "<hr><p>".join ' | ', (
231 '<a href="?'.(join '&', 101, '113-125', '127-134').'">Politik</a>',
232 '<a href="?'.(join '&', 102, '136-143' ).'">Chronik</a>',
233 '<a href="?'.(join '&', '701-709' ).'">Bundesländer</a>',
234 '<a href="?'.(join '&', '471-478', '481-488').'">einfache Sprache</a>',
235 '<a href="?'.(join '&', 103, '146-149' ).'">Leute</a>',
236 '<a href="?'.(join '&', 108, '461-465' ).'">Multimedia</a>',
237 '<a href="?'.(join '&', 110, 107, '191-198' ).'">Kultur &amp; Show</a>',
238 '<a href="?'.(join '&', 104, '151-159' ).'">Wirtschaft</a>',
239 '<a href="?'.(join '&', 161, 167, 169,170,172,174).'">Börse</a>',
240 '<a href="?'.(join '&', 105, '203-209' ).'">Sport</a>',
241 '<a href="?'.(join '&', '101-110' ).'">Topstories</a>',
242 );
243 print "</section>";
244 print "</article>";
Imprint / Impressum