summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xBuild.PL5
-rw-r--r--Dockerfile73
-rw-r--r--README.md122
-rw-r--r--cpanfile12
-rw-r--r--examples/vrr-infoscreen.service21
-rw-r--r--index.pl215
-rw-r--r--public/static/502.html20
-rw-r--r--public/static/504.html20
-rw-r--r--public/static/css/display.css (renamed from public/static/display.css)2
-rw-r--r--public/static/css/infoscreen.css (renamed from public/static/infoscreen.css)0
-rw-r--r--public/static/css/main.css (renamed from public/static/main.css)0
-rw-r--r--public/static/css/mobile.css (renamed from public/static/mobile.css)0
-rw-r--r--public/static/fonts/VRRR.ttf (renamed from public/static/VRRR.ttf)bin139372 -> 139372 bytes
-rw-r--r--public/static/js/collapse.js (renamed from public/static/collapse.js)0
-rw-r--r--public/static/js/image.js (renamed from public/static/image.js)0
-rw-r--r--public/static/js/jquery-2.1.1.min.js (renamed from public/static/jquery-2.1.1.min.js)0
l---------public/static/v11
-rw-r--r--templates/display.html.ep5
-rw-r--r--templates/exception.html.ep3
-rw-r--r--templates/infoscreen.html.ep20
-rw-r--r--templates/main.html.ep50
-rw-r--r--templates/not_found.html.ep3
22 files changed, 463 insertions, 109 deletions
diff --git a/Build.PL b/Build.PL
index ef1f625..d46cfb2 100755
--- a/Build.PL
+++ b/Build.PL
@@ -25,8 +25,9 @@ Module::Build->new(
'DateTime::Format::Strptime' => 0,
'GD' => 0,
'Mojolicious::Lite' => 0,
- 'Travel::Status::DE::DeutscheBahn' => 1.01,
- 'Travel::Status::DE::VRR' => 1.04,
+ 'Travel::Status::DE::EFA' => 1.15,
+ 'Travel::Status::DE::HAFAS' => 2.03,
+ 'Travel::Status::DE::URA' => 2.01,
},
sign => 1,
dist_version_from => 'index.pl',
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..b208156
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,73 @@
+# docker build -t vrr-fakedisplay:latest --build-arg=vrrf_version=$(git describe --dirty) .
+
+FROM debian:buster-slim AS files
+
+ARG vrrf_version=git
+
+COPY Build.PL cpanfile index.pl /app/
+COPY lib/ /app/lib/
+COPY public/ /app/public/
+COPY share/ /app/share/
+COPY templates/ /app/templates/
+
+WORKDIR /app
+
+RUN ln -sf ../ext-templates/imprint.html.ep templates/imprint.html.ep \
+ && ln -sf ../ext-templates/privacy.html.ep templates/privacy.html.ep
+
+RUN find lib -name '*.pm' -or -name '*.PL' | xargs sed -i \
+ -e "s/VERSION *= *.*;/VERSION = '${vrrf_version}';/"
+
+RUN sed -i -e "s/VERSION *= *.*;/VERSION = '${vrrf_version}';/" \
+ index.pl
+
+
+FROM perl:5.30-slim
+
+ARG DEBIAN_FRONTEND=noninteractive
+ARG APT_LISTCHANGES_FRONTEND=none
+
+COPY --from=files /app/ /app/
+
+WORKDIR /app
+
+RUN apt-get update \
+ && apt-get -y --no-install-recommends install \
+ ca-certificates \
+ curl \
+ gcc \
+ libc6-dev \
+ libdb5.3 \
+ libdb5.3-dev \
+ libgd3 \
+ libgd-dev \
+ libssl1.1 \
+ libssl-dev \
+ libxml2 \
+ libxml2-dev \
+ make \
+ zlib1g-dev \
+ && cpanm -n --no-man-pages --installdeps . \
+ && mv public / \
+ && perl Build.PL \
+ && perl Build \
+ && perl Build manifest \
+ && perl Build install \
+ && rm -rf ~/.cpanm _build blib MANIFEST* META* MYMETA* \
+ && mv /public . \
+ && apt-get -y purge \
+ curl \
+ gcc \
+ libc6-dev \
+ libdb5.3-dev -\
+ libgd-dev \
+ libssl-dev \
+ libxml2-dev \
+ make \
+ zlib1g-dev \
+ && apt-get -y autoremove \
+ && rm -rf /var/cache/apt/* /var/lib/apt/lists/*
+
+EXPOSE 8091
+
+CMD ["hypnotoad", "-f", "index.pl"]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..06637f8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,122 @@
+# vrr-infoscreen - Infoscreen for Public Transit Departures
+
+[vrr-infoscreen homepage](https://finalrewind.org/projects/vrr-fakedisplay/)
+
+vrr-infoscreen (formerly vrr-fakedisplay) shows departures at a public transit
+stop, serving both as infoscreen / webapp and LED departure monitor look-alike.
+
+It supports most german local transit networks and also some austrian ones.
+
+There's a public [vrr-infoscreen service on
+finalrewind.org](https://vrrf.finalrewind.org/). You can also host your own
+instance via carton/cpanminus or Docker, see the Setup notes below.
+
+**Legacy Warning**. As of 2017, vrr-infoscreen is not under active development
+anymore. Maintenance and bugfixes may not occur in a timely manner.
+
+## Dependencies
+
+ * perl ≥ 5.20
+ * carton or cpanminus
+ * build-essential (gcc/clang, make)
+ * libdb (Berkeley Database Libraries)
+ * libgd (GD Graphics Library)
+ * libxml2
+ * zlib
+
+## Installation
+
+After installing the dependencies, clone the repository using git, e.g.
+
+```
+git clone https://git.finalrewind.org/vrr-fakedisplay
+```
+
+Make sure that all files (including `.git`, which is used to determine the
+software version) are readable by your www user, and follow the steps in the
+next sections.
+
+## Perl Dependencies
+
+vrr-infoscreen depends on a set of Perl modules which are documented in
+`cpanfile`. After installing the dependencies mentioned above, you can use
+carton or cpanminus to install Perl depenencies locally.
+
+In the project root directory (where `cpanfile` resides), run either
+
+```
+carton install
+```
+
+or
+
+```
+cpanm -n --installdeps .
+```
+
+Next, you need to build App::VRR::Fakedisplay, which is required for the LED
+frontend and shipped with vrr-fakedisplay.
+
+```
+export PERL5LIB=local/lib/perl5
+perl Build.PL
+./Build
+./Build manifest
+sudo ./Build install
+```
+
+## Running
+
+You are now ready to start the web service. If you used carton, it boils
+down to
+
+```
+carton exec hypnotoad index.pl
+```
+
+Otherwise, you need to make the perl dependencies available by setting the
+PERL5LIB environment variable:
+
+```
+PERL5LIB=local/lib/perl5 local/bin/hypnotoad index.pl
+```
+
+Note that you should provide imprint and privacy policy pages. Depending on
+traffic volume, you may also want to increase the amount of worker processes.
+See the Setup notes below.
+
+## Installation with Docker
+
+A vrr-infoscreen image is available on Docker Hub. You can install and run it
+as follows:
+
+```
+docker pull derfnull/vrr-fakedisplay:latest
+docker run --rm -p 8000:8091 -v "$(pwd)/templates:/app/ext-templates:ro" vrr-fakedisplay:latest
+```
+
+This will make the web service available on port 8000. Note that you should
+provide imprint and privacy policy pages, see the Setup notes below.
+
+Use `docker run -e VRRFAKEDISPLAY_WORKERS=4 ...` and similar to pass
+environment variables to the vrr-infoscreen service.
+
+## Setup
+
+vrr-infoscreen is configured via environment variables:
+
+| Variable | Default | Description |
+| :------- | :------ | :---------- |
+| VRRFAKEDISPLAY\_LISTEN | `http://*:8091` | IP and Port for web service |
+| VRRFAKEDISPLAY\_STATS | _None_ | File in which the total count of (non-cached) backend API requests is written |
+| VRRFAKEDISPLAY\_CACHE | `/tmp/vrr-fakedisplay` | Cache directory |
+| VRRFAKEDISPLAY\_WORKERS | 2 | Number of concurrent worker processes |
+
+Set these as needed, create `templates/imprint.html.ep` (imprint) and
+`templates/privacy.html.ep` (privacy policy), and configure your web server to
+pass requests for vrr-infoscreen to the appropriate port.
+
+You can run the app using a Mojo::Server of your choice, e.g. **perl
+index.pl daemon -m production** (quick&dirty, does not respect all variables)
+or **hypnotad** (recommended). A systemd unit example is provided in
+`examples/vrr-infoscreen.service`.
diff --git a/cpanfile b/cpanfile
new file mode 100644
index 0000000..49198e0
--- /dev/null
+++ b/cpanfile
@@ -0,0 +1,12 @@
+requires 'Cache';
+requires 'DateTime';
+requires 'DateTime::Format::Strptime';
+requires 'File::ShareDir';
+requires 'File::Slurp';
+requires 'GD';
+requires 'Module::Build';
+requires 'Mojolicious';
+requires 'Mojolicious::Plugin::BrowserDetect';
+requires 'Travel::Status::DE::EFA';
+requires 'Travel::Status::DE::HAFAS';
+requires 'Travel::Status::DE::URA';
diff --git a/examples/vrr-infoscreen.service b/examples/vrr-infoscreen.service
new file mode 100644
index 0000000..39b3171
--- /dev/null
+++ b/examples/vrr-infoscreen.service
@@ -0,0 +1,21 @@
+[Unit]
+Description=vrrf.finalrewind.org
+After=network.target
+
+[Service]
+Type=simple
+RemainAfterExit=yes
+PIDFile=/tmp/vrr-fakedisplay.pid
+ExecStart=/usr/bin/hypnotoad -f index.pl
+ExecStop=/usr/bin/hypnotoad -s index.pl
+ExecReload=/usr/bin/hypnotoad index.pl
+User=vrr-fakedisplay
+WorkingDirectory=/srv/www/vrr-fakedisplay
+Environment=LANG=en_US.UTF-8
+Environment=VRRFAKEDISPLAY_LISTEN=http://127.0.0.1:8091
+Environment=VRRFAKEDISPLAY_WORKERS=1
+Environment=VRRFAKEDISPLAY_STATS=/tmp/vrrf-api-stats
+Environment=VRRFAKEDISPLAY_CACHE=/var/cache/vrrf/main
+
+[Install]
+WantedBy=multi-user.target
diff --git a/index.pl b/index.pl
index 01dd760..676bb19 100644
--- a/index.pl
+++ b/index.pl
@@ -11,7 +11,7 @@ use List::MoreUtils qw();
use App::VRR::Fakedisplay;
use Travel::Status::DE::HAFAS;
-use Travel::Status::DE::ASEAG;
+use Travel::Status::DE::URA;
use Travel::Status::DE::EFA;
no warnings 'uninitialized';
@@ -34,6 +34,9 @@ my @efa_services
my @hafas_services
= map { $_->{shortname} } Travel::Status::DE::HAFAS::get_services();
+my @ura_services
+ = map { $_->{shortname} } Travel::Status::DE::URA::get_services();
+
sub log_api_access {
my $counter = 1;
if ( -r $ENV{VRRFAKEDISPLAY_STATS} ) {
@@ -43,7 +46,10 @@ sub log_api_access {
}
sub get_results {
- my ( $backend, $city, $stop ) = @_;
+ my %opt = @_;
+ my $backend = $opt{backend};
+ my $city = $opt{city};
+ my $stop = $opt{stop};
my $sub_backend;
my $expiry = 200;
@@ -55,6 +61,9 @@ sub get_results {
if ( $backend and $backend eq 'db' ) {
$backend = 'hafas.DB';
}
+ if ( $backend and $backend eq 'aseag' ) {
+ $backend = 'ura.ASEAG';
+ }
if ( $backend =~ s{ [.] (.+) $ }{}x ) {
$sub_backend = $1;
@@ -71,6 +80,12 @@ sub get_results {
errstr => "hafas sub-backend '$sub_backend' not supported"
};
}
+ if ( $backend eq 'ura' and not $sub_backend ~~ \@ura_services ) {
+ return {
+ results => [],
+ errstr => "ura sub-backend '$sub_backend' not supported"
+ };
+ }
}
if ( $backend eq 'hafas' ) {
@@ -81,7 +96,7 @@ sub get_results {
}
my $cache = Cache::File->new(
- cache_root => $ENV{VRRFAKEDISPLAY_CACHE} // '/tmp/vrr-fakedisplay',
+ cache_root => $ENV{VRRFAKEDISPLAY_CACHE} // '/tmp/vrr-fakedisplay',
default_expires => "${expiry} sec",
lock_level => Cache::File::LOCK_LOCAL(),
);
@@ -98,22 +113,26 @@ sub get_results {
my $status;
if ( $backend eq 'hafas' ) {
$status = Travel::Status::DE::HAFAS->new(
- station => ( $city ? "${city} ${stop}" : $stop ),
+ station => ( $city ? "${city} ${stop}" : $stop ),
excluded_mots => [qw[ice ic_ec d regio]],
service => $sub_backend,
);
}
- elsif ( $backend eq 'aseag' ) {
- $status = Travel::Status::DE::ASEAG->new(
- stop => ( $city ? "${city} ${stop}" : $stop ),
- calculate_routes => 1,
- );
+ elsif ( $backend eq 'ura' ) {
+ my $service
+ = first { lc( $_->{shortname} ) eq lc($sub_backend) }
+ Travel::Status::DE::URA::get_services();
+ if ($service) {
+ $status = Travel::Status::DE::URA->new(
+ ura_base => $service->{ura_base},
+ ura_version => $service->{ura_version},
+ stop => ( $city ? "${city} ${stop}" : $stop ),
+ calculate_routes => 1,
+ );
+ }
}
else {
my $efa_url = 'http://efa.vrr.de/vrr/XSLT_DM_REQUEST';
- if ( not $city ) {
- return { errstr => 'City must be specified for this backend' };
- }
if ($sub_backend) {
my $service
= first { lc( $_->{shortname} ) eq lc($sub_backend) }
@@ -123,11 +142,12 @@ sub get_results {
}
}
$status = Travel::Status::DE::EFA->new(
- efa_url => $efa_url,
- place => $city,
- name => $stop,
- timeout => 3,
- full_routes => 0,
+ efa_url => $efa_url,
+ place => $city,
+ name => $stop,
+ timeout => 3,
+ full_routes => 0,
+ proximity_search => $opt{proximity_search}
);
}
if ( not $status->errstr ) {
@@ -173,8 +193,11 @@ sub handle_request {
my $data;
if ($stop) {
- $data = get_results( $self->param('backend') // $default{backend},
- $city, $stop );
+ $data = get_results(
+ backend => $self->param('backend') // $default{backend},
+ city => $city,
+ stop => $stop
+ );
}
if ( not $no_lines or $no_lines < 1 or $no_lines > 40 ) {
@@ -254,7 +277,12 @@ sub get_filtered_departures {
my ( @grep_line, @grep_platform, @filtered_results );
- my $data = get_results( $opt{backend}, $opt{city}, $opt{stop} );
+ my $data = get_results(
+ backend => $opt{backend},
+ city => $opt{city},
+ stop => $opt{stop},
+ proximity_search => $opt{proximity_search}
+ );
my $results = $data->{results};
@@ -284,9 +312,9 @@ sub get_filtered_departures {
@grep_line
and not( List::MoreUtils::any { $line =~ $_ } @grep_line )
)
- or ( @grep_platform and not( $platform ~~ \@grep_platform ) )
+ or ( @grep_platform and not( $platform ~~ \@grep_platform ) )
or ( $opt{hide_regional} and $line =~ m{ ^ (RB | RE | IC | EC) }x )
- or ( $opt{offset} and $d->countdown < $opt{offset} )
+ or ( $opt{offset} and $d->countdown < $opt{offset} )
)
{
next;
@@ -304,15 +332,16 @@ sub make_infoboard_lines {
my (%opt) = @_;
my ( @grep_line, @grep_platform );
- my $no_lines = $opt{no_lines} // $default{no_lines};
- my $max_lines = $opt{max_lines} // 40;
- my $offset = $opt{offset} // 0;
- my $results = $opt{data};
+ my $no_lines = $opt{no_lines} // $default{no_lines};
+ my $max_lines = $opt{max_lines} // 40;
+ my $offset = $opt{offset} // 0;
+ my $time_format = $opt{time_format} // 'countdown';
+ my $results = $opt{data};
my $displayed_lines = 0;
my $want_crop = $opt{want_crop};
my @fmt_departures;
- my $dt_now = DateTime->now( time_zone => 'Europe/Berlin' );
+ my $dt_now = DateTime->now( time_zone => 'Europe/Berlin' );
my $strp_simple = DateTime::Format::Strptime->new(
pattern => '%H:%M',
time_zone => 'floating',
@@ -371,6 +400,9 @@ sub make_infoboard_lines {
{
next;
}
+ elsif ( $time_format eq 'hhmm' ) {
+ $etr = $dt->strftime('%H:%M');
+ }
elsif ( $duration->in_units('minutes') == 0 ) {
$etr = 'sofort';
}
@@ -407,23 +439,25 @@ sub render_html {
my $template = $frontend eq 'html' ? 'display' : 'infoscreen';
my $data = get_filtered_departures(
- city => $self->stash('city') // q{},
- stop => $self->stash('stop'),
- backend => scalar $self->param('backend'),
- filter_line => scalar $self->param('line'),
- filter_platform => scalar $self->param('platform'),
- hide_regional => ( $template eq 'infoscreen' ? 0 : 1 ),
- offset => scalar $self->param('offset'),
+ city => $self->stash('city') // q{},
+ stop => $self->stash('stop'),
+ backend => scalar $self->param('backend'),
+ filter_line => scalar $self->param('line'),
+ filter_platform => scalar $self->param('platform'),
+ hide_regional => ( $template eq 'infoscreen' ? 0 : 1 ),
+ offset => scalar $self->param('offset'),
+ proximity_search => scalar $self->param('proximity_search'),
);
my @departures = make_infoboard_lines(
- city => $self->stash('city') // q{},
- stop => $self->stash('stop'),
- backend => scalar $self->param('backend'),
- no_lines => scalar $self->param('no_lines'),
- offset => scalar $self->param('offset'),
- want_crop => scalar $self->param('want_crop'),
- data => $data->{filtered_results},
+ city => $self->stash('city') // q{},
+ stop => $self->stash('stop'),
+ backend => scalar $self->param('backend'),
+ no_lines => scalar $self->param('no_lines'),
+ offset => scalar $self->param('offset'),
+ time_format => scalar $self->param('time_format'),
+ want_crop => scalar $self->param('want_crop'),
+ data => $data->{filtered_results},
);
for my $d (@departures) {
@@ -451,26 +485,30 @@ sub render_html {
sub render_json {
my $self = shift;
+ my $time_format = $self->param('time_format') // 'countdown';
+
my $data = get_filtered_departures(
- city => $self->stash('city') // q{},
- stop => $self->stash('stop'),
- backend => scalar $self->param('backend'),
- filter_line => scalar $self->param('line'),
- filter_platform => scalar $self->param('platform'),
- hide_regional => 0,
- offset => scalar $self->param('offset'),
+ city => $self->stash('city') // q{},
+ stop => $self->stash('stop'),
+ backend => scalar $self->param('backend'),
+ filter_line => scalar $self->param('line'),
+ filter_platform => scalar $self->param('platform'),
+ hide_regional => 0,
+ offset => scalar $self->param('offset'),
+ proximity_search => scalar $self->param('proximity_search'),
);
my $raw_departures = $data->{filtered_results};
my $errstr = $data->{errstr};
my @departures = make_infoboard_lines(
- no_lines => scalar $self->param('no_lines'),
- offset => scalar $self->param('offset'),
- want_crop => scalar $self->param('want_crop'),
- data => $raw_departures,
+ no_lines => scalar $self->param('no_lines'),
+ offset => scalar $self->param('offset'),
+ time_format => $time_format,
+ want_crop => scalar $self->param('want_crop'),
+ data => $raw_departures,
);
for my $d (@departures) {
- if ( $d->[2] and $d->[2] ne 'sofort' ) {
+ if ( $d->[2] and $d->[2] ne 'sofort' and $time_format eq 'countdown' ) {
$d->[2] .= ' min';
}
}
@@ -491,29 +529,32 @@ sub render_json {
sub render_image {
my $self = shift;
- my $color = $self->param('color') || '255,208,0';
- my $scale = $self->param('scale');
+ my $color = $self->param('color') || '255,208,0';
+ my $scale = $self->param('scale');
+ my $time_format = $self->param('time_format') // 'countdown';
my $data = get_filtered_departures(
- city => $self->stash('city') // q{},
- stop => $self->stash('stop'),
- backend => scalar $self->param('backend'),
- filter_line => scalar $self->param('line'),
- filter_platform => scalar $self->param('platform'),
- hide_regional => 0,
- offset => scalar $self->param('offset'),
+ city => $self->stash('city') // q{},
+ stop => $self->stash('stop'),
+ backend => scalar $self->param('backend'),
+ filter_line => scalar $self->param('line'),
+ filter_platform => scalar $self->param('platform'),
+ hide_regional => 0,
+ offset => scalar $self->param('offset'),
+ proximity_search => scalar $self->param('proximity_search'),
);
my $raw_departures = $data->{filtered_results};
my $errstr = $data->{errstr};
my @departures = make_infoboard_lines(
- city => $self->stash('city') // q{},
- stop => $self->stash('stop'),
- backend => scalar $self->param('backend'),
- no_lines => scalar $self->param('no_lines'),
- offset => scalar $self->param('offset'),
- want_crop => scalar $self->param('want_crop'),
- data => $raw_departures
+ city => $self->stash('city') // q{},
+ stop => $self->stash('stop'),
+ backend => scalar $self->param('backend'),
+ no_lines => scalar $self->param('no_lines'),
+ offset => scalar $self->param('offset'),
+ time_format => $time_format,
+ want_crop => scalar $self->param('want_crop'),
+ data => $raw_departures
);
if ( $scale > 30 ) {
@@ -548,7 +589,10 @@ sub render_image {
$png->draw_at( 0, $line );
$png->draw_at( 25, $destination );
- if ( length($etr) > 2 ) {
+ if ( $time_format eq 'hhmm' ) {
+ $png->draw_at( 153, $etr );
+ }
+ elsif ( length($etr) > 2 ) {
$png->draw_at( 145, $etr );
}
elsif ( length($etr) > 1 ) {
@@ -558,7 +602,7 @@ sub render_image {
$png->draw_at( 154, $etr );
}
- if ( $etr and $etr ne 'sofort' ) {
+ if ( $etr and $etr ne 'sofort' and $time_format eq 'countdown' ) {
$png->draw_at( 161, 'min' );
}
@@ -587,6 +631,12 @@ helper 'hafas_service_list' => sub {
return @hafas_services;
};
+helper 'ura_service_list' => sub {
+ my $self = shift;
+
+ return @ura_services;
+};
+
helper 'handle_no_results' => sub {
};
@@ -612,8 +662,11 @@ get '/_redirect' => sub {
if ( $params->param('frontend')
and $params->param('frontend') eq 'infoscreen' )
{
- my $data = get_results( $self->param('backend') // $default{backend},
- $city, $stop );
+ my $data = get_results(
+ backend => $self->param('backend') // $default{backend},
+ city => $city,
+ stop => $stop
+ );
if ( not $data->{errstr} ) {
$suffix = '.html';
}
@@ -652,18 +705,18 @@ get '/_imprint' => sub {
};
get '/' => \&handle_request;
-get '/:city/(*stop).html' => \&render_html;
-get '/:city/(*stop).json' => \&render_json;
-get '/:city/(*stop).png' => \&render_image;
+get '/:city/<*stop>.html' => \&render_html;
+get '/:city/<*stop>.json' => \&render_json;
+get '/:city/<*stop>.png' => \&render_image;
get '/:city/*stop' => \&handle_request;
-get '/(:stop).html' => \&render_html;
-get '/(:stop).json' => \&render_json;
-get '/(:stop).png' => \&render_image;
-get '/:stop' => \&handle_request;
+get '/<*stop>.html' => \&render_html;
+get '/<*stop>.json' => \&render_json;
+get '/<*stop>.png' => \&render_image;
+get '/*stop' => \&handle_request;
app->config(
hypnotoad => {
- listen => [ $ENV{VRRFAKEDISPLAY_LISTEN} // 'http://127.0.0.1:8091' ],
+ listen => [ $ENV{VRRFAKEDISPLAY_LISTEN} // 'http://*:8091' ],
pid_file => '/tmp/vrr-fakedisplay.pid',
workers => $ENV{VRRFAKEDISPLAY_WORKERS} // 2,
},
diff --git a/public/static/502.html b/public/static/502.html
new file mode 100644
index 0000000..7702a87
--- /dev/null
+++ b/public/static/502.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>vrr-infoscreen: 502 Bad Gateway</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link href="/static/main.css" rel="stylesheet">
+</head>
+<body>
+
+<div class="container">
+<div class="error">
+<strong>502 Bad Gateway</strong>
+Das Backend konnte die Anfrage nicht bearbeiten.<br/>
+Mögliche Gründe dafür sind Programmierfehler oder Nichterreichbarkeit der
+Nameserver von <a href="http://vrr.de">vrr.de</a>.
+</div> <!-- error -->
+</div> <!-- container -->
+</body>
+</html>
diff --git a/public/static/504.html b/public/static/504.html
new file mode 100644
index 0000000..2298cd0
--- /dev/null
+++ b/public/static/504.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>vrr-infoscreen: 504 Gateway Timeout</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link href="/static/main.css" rel="stylesheet">
+</head>
+<body>
+
+<div class="container">
+<div class="error">
+<strong>504 Gateway Timeout</strong>
+Das Backend konnte die Anfrage nicht rechtzeitig bearbeiten.<br/>
+Mögliche Gründe dafür sind Programmierfehler oder Nichterreichbarkeit der
+Nameserver von <a href="http://vrr.de">vrr.de</a>.
+</div> <!-- error -->
+</div> <!-- container -->
+</body>
+</html>
diff --git a/public/static/display.css b/public/static/css/display.css
index f874bf2..76280c5 100644
--- a/public/static/display.css
+++ b/public/static/css/display.css
@@ -1,6 +1,6 @@
@font-face {
font-family: VRRR;
- src: url('/static/VRRR.ttf');
+ src: url('/static/v1/fonts/VRRR.ttf');
}
body {
diff --git a/public/static/infoscreen.css b/public/static/css/infoscreen.css
index c2415be..c2415be 100644
--- a/public/static/infoscreen.css
+++ b/public/static/css/infoscreen.css
diff --git a/public/static/main.css b/public/static/css/main.css
index 7eee1f3..7eee1f3 100644
--- a/public/static/main.css
+++ b/public/static/css/main.css
diff --git a/public/static/mobile.css b/public/static/css/mobile.css
index efa6023..efa6023 100644
--- a/public/static/mobile.css
+++ b/public/static/css/mobile.css
diff --git a/public/static/VRRR.ttf b/public/static/fonts/VRRR.ttf
index e532b93..e532b93 100644
--- a/public/static/VRRR.ttf
+++ b/public/static/fonts/VRRR.ttf
Binary files differ
diff --git a/public/static/collapse.js b/public/static/js/collapse.js
index d38b30a..d38b30a 100644
--- a/public/static/collapse.js
+++ b/public/static/js/collapse.js
diff --git a/public/static/image.js b/public/static/js/image.js
index 40bc37d..40bc37d 100644
--- a/public/static/image.js
+++ b/public/static/js/image.js
diff --git a/public/static/jquery-2.1.1.min.js b/public/static/js/jquery-2.1.1.min.js
index e5ace11..e5ace11 100644
--- a/public/static/jquery-2.1.1.min.js
+++ b/public/static/js/jquery-2.1.1.min.js
diff --git a/public/static/v1 b/public/static/v1
new file mode 120000
index 0000000..945c9b4
--- /dev/null
+++ b/public/static/v1
@@ -0,0 +1 @@
+. \ No newline at end of file
diff --git a/templates/display.html.ep b/templates/display.html.ep
index 4cf52a2..5a98e1a 100644
--- a/templates/display.html.ep
+++ b/templates/display.html.ep
@@ -3,8 +3,9 @@
<head>
<title><%= $title %></title>
<meta charset="utf-8">
- %= stylesheet '/static/display.css'
- %= javascript '/static/jquery-2.1.1.min.js'
+ % my $av = 'v1'; # asset version
+ %= stylesheet "/static/${av}/css/display.css"
+ %= javascript '/static/js/jquery-2.1.1.min.js'
</head>
<body style="color: #<%= join(q{}, map { sprintf('%02x', $_) } @{$color} ) %>;
font-size: <%= $scale * 100 %>%;">
diff --git a/templates/exception.html.ep b/templates/exception.html.ep
index 2312586..aea4c40 100644
--- a/templates/exception.html.ep
+++ b/templates/exception.html.ep
@@ -4,7 +4,8 @@
<title>vrr-infoscreen</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- %= stylesheet '/static/main.css'
+ % my $av = 'v1'; # asset version
+ %= stylesheet "/static/${av}/css/main.css"
</head>
<body>
diff --git a/templates/infoscreen.html.ep b/templates/infoscreen.html.ep
index d634a93..62aa56c 100644
--- a/templates/infoscreen.html.ep
+++ b/templates/infoscreen.html.ep
@@ -8,12 +8,13 @@
<meta http-equiv="refresh" content="<%= $self->stash('refresh_interval') %>"/>
% }
- %= stylesheet '/static/infoscreen.css'
+ % my $av = 'v1'; # asset version
+ %= stylesheet "/static/${av}/css/infoscreen.css"
% if ($self->browser->mobile) {
- %= stylesheet '/static/mobile.css'
+ %= stylesheet "/static/${av}/css/mobile.css"
% }
- %= javascript '/static/jquery-2.1.1.min.js'
- %= javascript '/static/collapse.js'
+ %= javascript '/static/js/jquery-2.1.1.min.js'
+ %= javascript "/static/${av}/js/collapse.js"
</head>
<body>
@@ -180,6 +181,17 @@
% if ($departure->can('route_interesting') and $departure->route_interesting) {
%= join(' - ', map { $_->can('name_suf') ? $_->name_suf : $_->name } ($departure->route_interesting));
% }
+% elsif ($departure->can('occupancy') and $departure->occupancy) {
+% if ($departure->occupancy eq 'MANY_SEATS') {
+ ●○○
+% }
+% elsif ($departure->occupancy eq 'FEW_SEATS') {
+ ●●○
+% }
+% elsif ($departure->occupancy eq 'STANDING_ONLY') {
+ <span style="color: red;">●●●</span>
+% }
+% }
</span> <!-- route -->
<span class="dest">
%= $departure->destination
diff --git a/templates/main.html.ep b/templates/main.html.ep
index 4d843d1..8f21978 100644
--- a/templates/main.html.ep
+++ b/templates/main.html.ep
@@ -4,9 +4,10 @@
<title><%= $title %></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- %= stylesheet '/static/main.css'
- %= javascript '/static/jquery-2.1.1.min.js'
- %= javascript '/static/image.js'
+ % my $av = 'v1'; # asset version
+ %= stylesheet "/static/${av}/css/main.css"
+ %= javascript '/static/js/jquery-2.1.1.min.js'
+ %= javascript "/static/${av}/js/image.js"
</head>
<body>
@@ -55,7 +56,7 @@ Serialisierte
Travel::Status::DE::HAFAS</a>-Objekte, siehe die Dokumentation zu
<a href="http://man.finalrewind.org/3/Travel-Status-DE-HAFAS-Result/">::Result</a>
% }
-% elsif (param('backend') and param('backend') eq 'aseag') {
+% elsif (param('backend') and param('backend') =~ m{ ^ura }x) {
Serialisierte
<a href="http://finalrewind.org/projects/Travel-Status-DE-URA/">
Travel::Status::DE::URA</a>-Objekte, siehe die Dokumentation zu
@@ -80,10 +81,22 @@ Ankündigung ändern.</p>
% else {
<div class="container">
<p>
-Diese Seite ist ein inoffizielles Frontend für die Abfahrtsmonitore
-einiger europäischer Nahverkehrsunternehmen. Sie kannn wahlweise die oft an
-Haltestellen montierten LED-Displays nachahmen oder einen Handy- und
-Infoscreen-tauglichen Abfahrtsmonitor anzeigen.
+vrr-infoscreen ist ein inoffizielles Frontend für die Abfahrtsmonitore einiger
+europäischer Nahverkehrsunternehmen. Es kannn wahlweise die oft an Haltestellen
+montierten LED-Displays nachahmen oder einen für Infotafeln geeigneten
+Abfahrtsmonitor anzeigen.
+</p>
+
+<p>
+Diese Seite ist ein privat betriebenes Projekt ohne Verfügbarkeitsgarantie
+und wird derzeit nicht aktiv gepflegt oder weiterentwickelt. Alternativen:
+<ul>
+<li>LED-Tafel: Derzeit keine</li>
+<li>HTML/App: <a href="https://nvm.finalrewind.org">nvm.finalrewind.org</a></li>
+<li>JSON aus EFA-APIs: <a href="https://finalrewind.org/interblag/entry/efa-json-api/">EFA-APIs mit nativer JSON-Unterstützung</a></li>
+<li>JSON aus HAFAS-APIs: <a href="https://transport.rest/">transport.rest</a></li>
+<li>JSON aus URA-APIs: Derzeit keine</li>
+</ul>
</p>
<p>
@@ -103,7 +116,7 @@ Alle Angaben ohne Gewähr.
%= form_for _redirect => begin
<div>
<div class="field">
- <div class="desc">Stadt (Pflichtfeld für EFA-Backends, sonst optional)</div>
+ <div class="desc">Stadt (optional, kann auch Teil der Haltestelle sein)</div>
<div>
% if (stash('place_candidates') and @{ stash('place_candidates') } ) {
% my @candidates = map { [ $_, $_ ] } @{ stash('place_candidates') };
@@ -150,8 +163,11 @@ Alle Angaben ohne Gewähr.
% map { [$_->[0], $_] } @efa_backends;
% my @hafas_backends = map { ["$_ (HAFAS)" => "hafas.$_"] } hafas_service_list();
% @hafas_backends = map { $_->[1] } sort { $a->[0] cmp $b->[0] }
- % map { [$_->[0], $_] } @hafas_backends;
- %= select_field backend => [['EFA / VRR' => 'vrr'], ['DB (HAFAS)' => 'db'], ['ASEAG (URA)' => 'aseag'], @efa_backends, @hafas_backends]
+ % map { [$_->[0], $_] } @hafas_backends;
+ % my @ura_backends = map { ["$_ (URA)" => "ura.$_"] } ura_service_list();
+ % @ura_backends = map { $_->[1] } sort { $a->[0] cmp $b->[0] }
+ % map { [$_->[0], $_] } @ura_backends;
+ %= select_field backend => [['EFA / VRR' => 'vrr'], @efa_backends, @hafas_backends, @ura_backends]
</div>
</div>
<div class="field">
@@ -192,8 +208,8 @@ Alle Angaben ohne Gewähr.
<span class="notes">Hinweise:</span>
<ul>
<li><a href="/Essen/Martinstr?no_lines=8">Beispiel</a></li>
-<li><a href="http://dbf.finalrewind.org/">db-infoscreen</a>
-(für Züge und Bahnhöfe optimiert)</li>
+<li><a href="https://dbf.finalrewind.org/">db-infoscreen</a>
+(nur S-Bahn, Regional- und Fernverkehr)</li>
</ul>
</div> <!-- notes -->
@@ -201,12 +217,12 @@ Alle Angaben ohne Gewähr.
<a href="http://finalrewind.org/projects/vrr-fakedisplay/">vrr-infoscreen</a>
v<%= $version %><br/>
Backends:<br/>
-<a href="http://finalrewind.org/projects/Travel-Status-DE-URA/">Travel::Status::DE::URA</a>
-v<%= $Travel::Status::DE::ASEAG::VERSION %><br/>
-<a href="http://finalrewind.org/projects/Travel-Status-DE-DeutscheBahn/">Travel::Status::DE::HAFAS</a>
-v<%= $Travel::Status::DE::HAFAS::VERSION %><br/>
<a href="http://finalrewind.org/projects/Travel-Status-DE-VRR/">Travel::Status::DE::EFA</a>
v<%= $Travel::Status::DE::EFA::VERSION %><br/>
+<a href="http://finalrewind.org/projects/Travel-Status-DE-DeutscheBahn/">Travel::Status::DE::HAFAS</a>
+v<%= $Travel::Status::DE::HAFAS::VERSION %><br/>
+<a href="http://finalrewind.org/projects/Travel-Status-DE-URA/">Travel::Status::DE::URA</a>
+v<%= $Travel::Status::DE::URA::VERSION %><br/>
<a href="/_imprint">Impressum</a><br/>
<a href="/_privacy">Datenschutz</a><br/>
</div>
diff --git a/templates/not_found.html.ep b/templates/not_found.html.ep
index 1fada6c..98bb664 100644
--- a/templates/not_found.html.ep
+++ b/templates/not_found.html.ep
@@ -4,7 +4,8 @@
<title>vrr-infoscreen</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- %= stylesheet '/static/main.css'
+ % my $av = 'v1'; # asset version
+ %= stylesheet "/static/${av}/css/main.css"
</head>
<body>