diff options
Diffstat (limited to 'index.pl')
| -rw-r--r-- | index.pl | 1089 | 
1 files changed, 5 insertions, 1084 deletions
@@ -1,1090 +1,11 @@  #!/usr/bin/env perl  # Copyright (C) 2011-2018 Daniel Friesel <derf+dbf@finalrewind.org>  # License: 2-Clause BSD -use Mojolicious::Lite; -use Cache::File; -use File::Slurp qw(read_file write_file); -use List::Util qw(max); -use List::MoreUtils qw(); -use Travel::Status::DE::DBWagenreihung; -use Travel::Status::DE::HAFAS; -use Travel::Status::DE::HAFAS::StopFinder; -use Travel::Status::DE::IRIS; -use Travel::Status::DE::IRIS::Stations; -use 5.014; -use utf8; -no if $] >= 5.018, warnings => 'experimental::smartmatch'; +use strict; +use warnings; -our $VERSION = qx{git describe --dirty} || '0.05'; +use lib 'lib'; +use Mojolicious::Commands; -my %default = ( -	backend => 'iris', -	mode    => 'app', -	admode  => 'deparr', -); - -sub result_has_line { -	my ( $result, @lines ) = @_; -	my $line = $result->line; - -	if ( List::MoreUtils::any { $line =~ m{^$_} } @lines ) { -		return 1; -	} -	return 0; -} - -sub result_has_platform { -	my ( $result, @platforms ) = @_; -	my $platform = ( split( qr{ }, $result->platform // '' ) )[0]; - -	if ( List::MoreUtils::any { $_ eq $platform } @platforms ) { -		return 1; -	} -	return 0; -} - -sub result_has_train_type { -	my ( $result, @train_types ) = @_; -	my $train_type = $result->type; - -	if ( List::MoreUtils::any { $train_type =~ m{^$_} } @train_types ) { -		return 1; -	} -	return 0; -} - -sub result_has_via { -	my ( $result, $via ) = @_; - -	if ( not $result->can('route_post') ) { -		return 1; -	} - -	my @route = $result->route_post; - -	if ( List::MoreUtils::any { m{$via}i } @route ) { -		return 1; -	} -	return 0; -} - -sub log_api_access { -	my $counter = 1; -	if ( -r $ENV{DBFAKEDISPLAY_STATS} ) { -		$counter = read_file( $ENV{DBFAKEDISPLAY_STATS} ) + 1; -	} -	write_file( $ENV{DBFAKEDISPLAY_STATS}, $counter ); -	return; -} - -sub get_results_for { -	my ( $backend, $station, %opt ) = @_; -	my $data; - -	my $cache_hafas = Cache::File->new( -		cache_root      => $ENV{DBFAKEDISPLAY_HAFAS_CACHE} // '/tmp/dbf-hafas', -		default_expires => '180 seconds', -		lock_level      => Cache::File::LOCK_LOCAL(), -	); - -	my $cache_iris_main = Cache::File->new( -		cache_root => $ENV{DBFAKEDISPLAY_IRIS_CACHE} // '/tmp/dbf-iris-main', -		default_expires => '6 hours', -		lock_level      => Cache::File::LOCK_LOCAL(), -	); - -	my $cache_iris_rt = Cache::File->new( -		cache_root => $ENV{DBFAKEDISPLAY_IRISRT_CACHE} -		  // '/tmp/dbf-iris-realtime', -		default_expires => '70 seconds', -		lock_level      => Cache::File::LOCK_LOCAL(), -	); - -	# Cache::File has UTF-8 problems, so strip it (and any other potentially -	# problematic chars). -	my $cache_str = $station; -	$cache_str =~ tr{[0-9a-zA-Z -]}{}cd; - -	if ( $backend eq 'iris' ) { - -		if ( $ENV{DBFAKEDISPLAY_STATS} ) { -			log_api_access(); -		} - -		# requests with DS100 codes should be preferred (they avoid -		# encoding problems on the IRIS server). However, only use them -		# if we have an exact match. Ask the backend otherwise. -		my @station_matches -		  = Travel::Status::DE::IRIS::Stations::get_station($station); -		if ( @station_matches == 1 ) { -			$station = $station_matches[0][0]; -			my $status = Travel::Status::DE::IRIS->new( -				station        => $station, -				main_cache     => $cache_iris_main, -				realtime_cache => $cache_iris_rt, -				log_dir        => $ENV{DBFAKEDISPLAY_XMLDUMP_DIR}, -				lookbehind     => 20, -				%opt -			); -			$data = { -				results => [ $status->results ], -				errstr  => $status->errstr, -				station_name => -				  ( $status->station ? $status->station->{name} : $station ), -			}; -		} -		elsif ( @station_matches > 1 ) { -			$data = { -				results => [], -				errstr  => 'Ambiguous station name', -			}; -		} -		else { -			$data = { -				results => [], -				errstr  => 'Unknown station name', -			}; -		} -	} -	elsif ( $backend eq 'ris' ) { -		$data = $cache_hafas->thaw($cache_str); -		if ( not $data ) { -			if ( $ENV{DBFAKEDISPLAY_STATS} ) { -				log_api_access(); -			} -			my $status = Travel::Status::DE::HAFAS->new( -				station       => $station, -				excluded_mots => [qw[bus ferry ondemand tram u]], -				%opt -			); -			$data = { -				results => [ $status->results ], -				errstr  => $status->errstr, -			}; -			$cache_hafas->freeze( $cache_str, $data ); -		} -	} -	else { -		$data = { -			results => [], -			errstr  => "Backend '$backend' not supported", -		}; -	} - -	return $data; -} - -helper 'handle_no_results' => sub { -	my ( $self, $backend, $station, $errstr ) = @_; - -	if ( $backend eq 'ris' ) { -		my $db_service = Travel::Status::DE::HAFAS::get_service('DB'); -		my $sf         = Travel::Status::DE::HAFAS::StopFinder->new( -			url   => $db_service->{stopfinder}, -			input => $station, -		); -		my @candidates -		  = map { [ $_->{name}, $_->{id} ] } $sf->results; -		if ( @candidates > 1 -			or ( @candidates == 1 and $candidates[0][1] ne $station ) ) -		{ -			$self->render( -				'landingpage', -				stationlist => \@candidates, -				hide_opts   => 0 -			); -			return; -		} -	} -	if ( $backend eq 'iris' ) { -		my @candidates = map { [ $_->[1], $_->[0] ] } -		  Travel::Status::DE::IRIS::Stations::get_station($station); -		if ( @candidates > 1 -			or ( @candidates == 1 and $candidates[0][1] ne $station ) ) -		{ -			$self->render( -				'landingpage', -				stationlist => \@candidates, -				hide_opts   => 0 -			); -			return; -		} -	} -	$self->render( -		'landingpage', -		error     => ( $errstr // "Got no results for '$station'" ), -		hide_opts => 0 -	); -	return; -}; - -helper 'handle_no_results_json' => sub { -	my ( $self, $backend, $station, $errstr, $api_version, $callback ) = @_; - -	$self->res->headers->access_control_allow_origin(q{*}); -	my $json; -	if ($errstr) { -		$json = $self->render_to_string( -			json => { -				api_version => $api_version, -				version     => $VERSION, -				error       => $errstr, -			} -		); -	} -	else { -		my @candidates = map { { code => $_->[0], name => $_->[1] } } -		  Travel::Status::DE::IRIS::Stations::get_station($station); -		if ( @candidates > 1 -			or ( @candidates == 1 and $candidates[0]{code} ne $station ) ) -		{ -			$json = $self->render_to_string( -				json => { -					api_version => $api_version, -					version     => $VERSION, -					error       => 'ambiguous station code/name', -					candidates  => \@candidates, -				} -			); -		} -		else { -			$json = $self->render_to_string( -				json => { -					api_version => $api_version, -					version     => $VERSION, -					error => ( $errstr // "Got no results for '$station'" ) -				} -			); -		} -	} -	if ($callback) { -		$self->render( -			data   => "$callback($json);", -			format => 'json' -		); -	} -	else { -		$self->render( -			data   => $json, -			format => 'json' -		); -	} -	return; -}; - -helper 'is_important' => sub { -	my ( $self, $stop ) = @_; - -	# Centraal: dutch main station (Hbf in .nl) -	# HB:  swiss main station (Hbf in .ch) -	# hl.n.: czech main station (Hbf in .cz) -	if ( $stop =~ m{ HB $ | hl\.n\. $ | Hbf | Centraal | Flughafen }x ) { -		return 1; -	} -	return; -}; - -helper 'json_route_diff' => sub { -	my ( $self, $route, $sched_route ) = @_; -	my @json_route; -	my @route       = @{$route}; -	my @sched_route = @{$sched_route}; - -	my $route_idx = 0; -	my $sched_idx = 0; - -	while ( $route_idx <= $#route and $sched_idx <= $#sched_route ) { -		if ( $route[$route_idx] eq $sched_route[$sched_idx] ) { -			push( @json_route, { name => $route[$route_idx] } ); -			$route_idx++; -			$sched_idx++; -		} - -		# this branch is inefficient, but won't be taken frequently -		elsif ( not( $route[$route_idx] ~~ \@sched_route ) ) { -			push( -				@json_route, -				{ -					name         => $route[$route_idx], -					isAdditional => 1 -				} -			); -			$route_idx++; -		} -		else { -			push( -				@json_route, -				{ -					name        => $sched_route[$sched_idx], -					isCancelled => 1 -				} -			); -			$sched_idx++; -		} -	} -	while ( $route_idx < $#route ) { -		push( -			@json_route, -			{ -				name         => $route[$route_idx], -				isAdditional => 1, -				isCancelled  => 0 -			} -		); -		$route_idx++; -	} -	while ( $sched_idx < $#sched_route ) { -		push( -			@json_route, -			{ -				name         => $sched_route[$sched_idx], -				isAdditional => 0, -				isCancelled  => 1 -			} -		); -		$sched_idx++; -	} -	return @json_route; -}; - -sub handle_request { -	my $self    = shift; -	my $station = $self->stash('station'); -	my $via     = $self->param('via'); - -	my @platforms = split( /,/, $self->param('platforms') // q{} ); -	my @lines     = split( /,/, $self->param('lines') // q{} ); -	my $template  = $self->param('mode') // 'app'; -	my $hide_low_delay = $self->param('hidelowdelay') // 0; -	my $hide_opts      = $self->param('hide_opts') // 0; -	my $show_realtime  = $self->param('show_realtime') // 0; -	my $show_details   = $self->param('detailed') // 0; -	my $backend        = $self->param('backend') // 'iris'; -	my $admode         = $self->param('admode') // 'deparr'; -	my $dark_layout    = $self->param('dark') // 0; -	my $apiver         = $self->param('version') // 0; -	my $callback       = $self->param('callback'); -	my $with_related   = !$self->param('no_related'); -	my $save_defaults  = $self->param('save_defaults') // 0; -	my $limit          = $self->param('limit') // 0; -	my @train_types    = split( /,/, $self->param('train_types') // q{} ); -	my %opt; - -	my $api_version -	  = $backend eq 'iris' -	  ? $Travel::Status::DE::IRIS::VERSION -	  : $Travel::Status::DE::HAFAS::VERSION; - -	if ($save_defaults) { -		$self->session( has_data      => 1 ); -		$self->session( mode          => $template ); -		$self->session( hidelowdelay  => $hide_low_delay ); -		$self->session( hide_opts     => $hide_opts ); -		$self->session( show_realtime => $show_realtime ); -		$self->session( admode        => $admode ); -		$self->session( dark          => $dark_layout ); -		$self->session( detailed      => $show_details ); -		$self->session( no_related    => !$with_related ); -	} - -	$self->stash( departures => [] ); -	$self->stash( title      => 'db-infoscreen' ); -	$self->stash( version    => $VERSION ); - -	if ( defined $station and $station =~ s{ [.] txt $ }{}x ) { -		$template = 'text'; -		$self->param( station => $station ); -		$self->stash( layout => 'text' ); -	} -	elsif ( defined $station and $station =~ s{ [.] json $ }{}x ) { -		$template = 'json'; -	} - -	# Historically, there were two JSON APIs: 'json' (undocumented, raw -	# passthrough of serialized Travel::Status::DE::IRIS::Result / -	# Travel::Status::DE::DE::HAFAS::Result objects) and 'marudor' -	# (documented, IRIS only, stable versioned API). The latter was initially -	# created for marudor.de, but quickly used by other clients as well. -	# -	# marudor.de switched to a nodejs IRIS parser in December 2018. As the -	# 'json' API was not used and the 'marudor' variant is no longer related to -	# (or used by) marudor.de, it was renamed to 'json'. Many clients won't -	# notice this for year to come, so we make sure mode=marudor still works as -	# intended. -	if ( $template eq 'marudor' ) { -		$template = 'json'; -	} - -	if ( not( $template ~~ [qw[app infoscreen json multi single text]] ) ) { -		$template = 'app'; -	} - -	if ( not $station ) { -		if ( $self->session('has_data') ) { -			for my $param ( -				qw(mode hidelowdelay hide_opts show_realtime admode no_related dark detailed) -			  ) -			{ -				$self->param( $param => $self->session($param) ); -			} -		} -		$self->render( -			'landingpage', -			hide_opts  => 0, -			show_intro => 1 -		); -		return; -	} - -	if ( $template eq 'json' ) { -		$backend = 'iris'; -		$opt{lookahead} = 120; -	} - -	if ($with_related) { -		$opt{with_related} = 1; -	} - -	my @departures; -	my $data        = get_results_for( $backend, $station, %opt ); -	my $results_ref = $data->{results}; -	my $errstr      = $data->{errstr}; -	my @results     = @{$results_ref}; - -	if ( not @results and $template eq 'json' ) { -		$self->handle_no_results_json( $backend, $station, $errstr, -			$api_version, $callback ); -		return; -	} - -	# foo/bar used to mean "departures for foo via bar". This is now -	# deprecated, but most of these cases are handled here. -	if ( not @results and $station =~ m{/} ) { -		( $station, $via ) = split( qr{/}, $station ); -		$self->param( station => $station ); -		$self->param( via     => $via ); -		$data        = get_results_for( $backend, $station, %opt ); -		$results_ref = $data->{results}; -		$errstr      = $data->{errstr}; -		@results     = @{$results_ref}; -	} - -	if ( not @results ) { -		$self->handle_no_results( $backend, $station, $errstr ); -		return; -	} - -	if ( $template eq 'single' ) { -		if ( not @platforms ) { -			for my $result (@results) { -				if ( not( $result->platform ~~ \@platforms ) ) { -					push( @platforms, $result->platform ); -				} -			} -			@platforms = sort { $a <=> $b } @platforms; -		} -		my %pcnt; -		@results = grep { $pcnt{ $_->platform }++ < 1 } @results; -		@results = sort { $a->platform <=> $b->platform } @results; -	} - -	if ( $backend eq 'iris' and $show_realtime ) { -		if ( $admode eq 'arr' ) { -			@results = sort { -				( $a->arrival // $a->departure ) -				  <=> ( $b->arrival // $b->departure ) -			} @results; -		} -		else { -			@results = sort { -				( $a->departure // $a->arrival ) -				  <=> ( $b->departure // $b->arrival ) -			} @results; -		} -	} - -	if (@lines) { -		@results = grep { result_has_line( $_, @lines ) } @results; -	} - -	if (@platforms) { -		@results = grep { result_has_platform( $_, @platforms ) } @results; -	} - -	if ($via) { -		$via =~ s{ , \s* }{|}gx; -		@results = grep { result_has_via( $_, $via ) } @results; -	} - -	if (@train_types) { -		@results = grep { result_has_train_type( $_, @train_types ) } @results; -	} - -	if ( $limit and $limit =~ m{ ^ \d+ $ }x ) { -		splice( @results, $limit ); -	} - -	for my $result (@results) { -		my $platform = ( split( qr{ }, $result->platform // '' ) )[0]; -		my $delay = $result->delay; -		if ( $backend eq 'iris' and $admode eq 'arr' and not $result->arrival ) -		{ -			next; -		} -		if (    $backend eq 'iris' -			and $admode eq 'dep' -			and not $result->departure ) -		{ -			next; -		} -		my ( $info, $moreinfo ); -		if ( $backend eq 'iris' ) { -			my $delaymsg -			  = join( ', ', map { $_->[1] } $result->delay_messages ); -			my $qosmsg = join( ' +++ ', map { $_->[1] } $result->qos_messages ); -			if ( $result->is_cancelled ) { -				$info = "Fahrt fällt aus: ${delaymsg}"; -			} -			elsif ( $result->departure_is_cancelled ) { -				$info = "Zug endet hier: ${delaymsg}"; -			} -			elsif ( $result->delay and $result->delay > 0 ) { -				if ( $template eq 'app' or $template eq 'infoscreen' ) { -					$info = $delaymsg; -				} -				else { -					$info = sprintf( 'ca. +%d%s%s', -						$result->delay, $delaymsg ? q{: } : q{}, $delaymsg ); -				} -			} -			if (    $result->replacement_for -				and $template ne 'app' -				and $template ne 'infoscreen' ) -			{ -				for my $rep ( $result->replacement_for ) { -					$info = sprintf( -						'Ersatzzug für %s %s %s%s', -						$rep->type, $rep->train_no, -						$info ? '+++ ' : q{}, $info // q{} -					); -				} -			} -			if ( $info and $qosmsg ) { -				$info .= ' +++ '; -			} -			$info .= $qosmsg; - -			if ( $result->additional_stops and not $result->is_cancelled ) { -				my $additional_line = join( q{, }, $result->additional_stops ); -				$info -				  = 'Zusätzliche Halte: ' -				  . $additional_line -				  . ( $info ? ' +++ ' : q{} ) -				  . $info; -				if ( $template ne 'json' ) { -					push( -						@{$moreinfo}, -						[ 'Zusätzliche Halte', $additional_line ] -					); -				} -			} - -			if ( $result->canceled_stops and not $result->is_cancelled ) { -				my $cancel_line = join( q{, }, $result->canceled_stops ); -				$info -				  = 'Ohne Halt in: ' -				  . $cancel_line -				  . ( $info ? ' +++ ' : q{} ) -				  . $info; -				if ( $template ne 'json' ) { -					push( @{$moreinfo}, [ 'Ohne Halt in', $cancel_line ] ); -				} -			} - -			push( @{$moreinfo}, $result->messages ); -		} -		else { -			$info = $result->info; -			if ($info) { -				$moreinfo = [ [ 'HAFAS', $info ] ]; -			} -			if ( $result->delay and $result->delay > 0 ) { -				if ($info) { -					$info = 'ca. +' . $result->delay . ': ' . $info; -				} -				else { -					$info = 'ca. +' . $result->delay; -				} -			} -			push( @{$moreinfo}, map { [ 'HAFAS', $_ ] } $result->messages ); -		} - -		my $time = $result->time; - -		if ( $backend eq 'iris' ) { - -			# ->time defaults to dep, so we only need to overwrite $time -			# if we want arrival times -			if ( $admode eq 'arr' ) { -				$time = $result->sched_arrival->strftime('%H:%M'); -			} - -			if ($show_realtime) { -				if ( ( $admode eq 'arr' and $result->arrival ) -					or not $result->departure ) -				{ -					$time = $result->arrival->strftime('%H:%M'); -				} -				else { -					$time = $result->departure->strftime('%H:%M'); -				} -			} -		} - -		if ($hide_low_delay) { -			if ($info) { -				$info =~ s{ (?: ca [.] \s* )? [+] [ 1 2 3 4 ] $ }{}x; -			} -			if ( $delay and $delay < 5 ) { -				$delay = undef; -			} -		} -		if ($info) { -			$info =~ s{ (?: ca [.] \s* )? [+] (\d+) }{Verspätung ca $1 Min.}x; -		} - -		if ( $template eq 'json' ) { -			my @json_route = $self->json_route_diff( [ $result->route ], -				[ $result->sched_route ] ); - -			if ( $apiver == 1 ) { -				push( -					@departures, -					{ -						delay       => $delay, -						destination => $result->destination, -						isCancelled => $result->can('is_cancelled') -						? $result->is_cancelled -						: undef, -						messages => { -							delay => [ -								map { -									{ -										timestamp => $_->[0], -										text      => $_->[1] -									} -								} $result->delay_messages -							], -							qos => [ -								map { -									{ -										timestamp => $_->[0], -										text      => $_->[1] -									} -								} $result->qos_messages -							], -						}, -						platform          => $result->platform, -						route             => \@json_route, -						scheduledPlatform => $result->sched_platform, -						time              => $time, -						train             => $result->train, -						via               => [ $result->route_interesting(3) ], -					} -				); -			} -			elsif ( $apiver == 2 ) { -				my ( $delay_arr, $delay_dep, $sched_arr, $sched_dep ); -				if ( $result->arrival ) { -					$delay_arr = $result->arrival->subtract_datetime( -						$result->sched_arrival )->in_units('minutes'); -				} -				if ( $result->departure ) { -					$delay_dep = $result->departure->subtract_datetime( -						$result->sched_departure )->in_units('minutes'); -				} -				if ( $result->sched_arrival ) { -					$sched_arr = $result->sched_arrival->strftime('%H:%M'); -				} -				if ( $result->sched_departure ) { -					$sched_dep = $result->sched_departure->strftime('%H:%M'); -				} -				push( -					@departures, -					{ -						delayArrival   => $delay_arr, -						delayDeparture => $delay_dep, -						destination    => $result->destination, -						isCancelled    => $result->can('is_cancelled') -						? $result->is_cancelled -						: undef, -						messages => { -							delay => [ -								map { -									{ -										timestamp => $_->[0], -										text      => $_->[1] -									} -								} $result->delay_messages -							], -							qos => [ -								map { -									{ -										timestamp => $_->[0], -										text      => $_->[1] -									} -								} $result->qos_messages -							], -						}, -						platform           => $result->platform, -						route              => \@json_route, -						scheduledPlatform  => $result->sched_platform, -						scheduledArrival   => $sched_arr, -						scheduledDeparture => $sched_dep, -						train              => $result->train, -						via                => [ $result->route_interesting(3) ], -					} -				); -			} -			else {    # apiver == 3 -				my ( $delay_arr, $delay_dep, $sched_arr, $sched_dep ); -				if ( $result->arrival ) { -					$delay_arr = $result->arrival->subtract_datetime( -						$result->sched_arrival )->in_units('minutes'); -				} -				if ( $result->departure ) { -					$delay_dep = $result->departure->subtract_datetime( -						$result->sched_departure )->in_units('minutes'); -				} -				if ( $result->sched_arrival ) { -					$sched_arr = $result->sched_arrival->strftime('%H:%M'); -				} -				if ( $result->sched_departure ) { -					$sched_dep = $result->sched_departure->strftime('%H:%M'); -				} -				push( -					@departures, -					{ -						delayArrival   => $delay_arr, -						delayDeparture => $delay_dep, -						destination    => $result->destination, -						isCancelled    => $result->can('is_cancelled') -						? $result->is_cancelled -						: undef, -						messages => { -							delay => [ -								map { -									{ -										timestamp => $_->[0], -										text      => $_->[1] -									} -								} $result->delay_messages -							], -							qos => [ -								map { -									{ -										timestamp => $_->[0], -										text      => $_->[1] -									} -								} $result->qos_messages -							], -						}, -						platform           => $result->platform, -						route              => \@json_route, -						scheduledPlatform  => $result->sched_platform, -						scheduledArrival   => $sched_arr, -						scheduledDeparture => $sched_dep, -						train              => $result->train, -						trainClasses       => [ $result->classes ], -						trainNumber        => $result->train_no, -						via                => [ $result->route_interesting(3) ], -					} -				); -			} -		} -		elsif ( $template eq 'text' ) { -			push( -				@departures, -				[ -					sprintf( '%5s %s%s', -						$result->is_cancelled ? '--:--' : $time, -						( $delay and $delay > 0 ) ? q{+} : q{}, -						$delay || q{} ), -					$result->train, -					$result->destination, -					$platform // q{ } -				] -			); -		} -		elsif ( $backend eq 'iris' ) { -			push( -				@departures, -				{ -					time          => $time, -					sched_arrival => $result->sched_arrival -					? $result->sched_arrival->strftime('%H:%M') -					: undef, -					sched_departure => $result->sched_departure -					? $result->sched_departure->strftime('%H:%M') -					: undef, -					arrival => $result->arrival -					? $result->arrival->strftime('%H:%M') -					: undef, -					departure => $result->departure -					? $result->departure->strftime('%H:%M') -					: undef, -					train           => $result->train, -					train_type      => $result->type, -					train_line      => $result->line_no, -					train_no        => $result->train_no, -					via             => [ $result->route_interesting(3) ], -					scheduled_route => [ $result->sched_route ], -					route_pre       => [ $result->route_pre ], -					route_pre_diff  => [ -						$self->json_route_diff( -							[ $result->route_pre ], -							[ $result->sched_route_pre ] -						) -					], -					route_post      => [ $result->route_post ], -					route_post_diff => [ -						$self->json_route_diff( -							[ $result->route_post ], -							[ $result->sched_route_post ] -						) -					], -					destination            => $result->destination, -					origin                 => $result->origin, -					platform               => $result->platform, -					scheduled_platform     => $result->sched_platform, -					info                   => $info, -					is_cancelled           => $result->is_cancelled, -					departure_is_cancelled => $result->departure_is_cancelled, -					arrival_is_cancelled   => $result->arrival_is_cancelled, -					messages               => { -						delay => [ -							map { { timestamp => $_->[0], text => $_->[1] } } -							  $result->delay_messages -						], -						qos => [ -							map { { timestamp => $_->[0], text => $_->[1] } } -							  $result->qos_messages -						], -					}, -					moreinfo         => $moreinfo, -					delay            => $delay, -					additional_stops => [ $result->additional_stops ], -					canceled_stops   => [ $result->canceled_stops ], -					replaced_by      => [ -						map { $_->type . q{ } . $_->train_no } -						  $result->replaced_by -					], -					replacement_for => [ -						map { $_->type . q{ } . $_->train_no } -						  $result->replacement_for -					], -					wr_link => $result->sched_departure -					? $result->sched_departure->strftime('%Y%m%d%H%M') -					: undef, -				} -			); -		} -		else { -			push( -				@departures, -				{ -					time             => $time, -					train            => $result->train, -					train_type       => $result->type, -					destination      => $result->destination, -					platform         => $platform, -					changed_platform => $result->is_changed_platform, -					info             => $info, -					is_cancelled     => $result->can('is_cancelled') -					? $result->is_cancelled -					: undef, -					messages => { -						delay => [], -						qos   => [], -					}, -					moreinfo         => $moreinfo, -					delay            => $delay, -					additional_stops => [], -					canceled_stops   => [], -					replaced_by      => [], -					replacement_for  => [], -				} -			); -		} -	} - -	if ( $template eq 'json' ) { -		$self->res->headers->access_control_allow_origin(q{*}); -		my $json = $self->render_to_string( -			json => { -				departures => \@departures, -			} -		); -		if ($callback) { -			$self->render( -				data   => "$callback($json);", -				format => 'json' -			); -		} -		else { -			$self->render( -				data   => $json, -				format => 'json' -			); -		} -	} -	elsif ( $template eq 'text' ) { -		my @line_length; -		for my $i ( 0 .. $#{ $departures[0] } ) { -			$line_length[$i] = max map { length( $_->[$i] ) } @departures; -		} -		my $output = q{}; -		for my $departure (@departures) { -			$output .= sprintf( -				join( q{  }, ( map { "%-${_}s" } @line_length ) ) . "\n", -				@{$departure}[ 0 .. $#{$departure} ] -			); -		} -		$self->render( -			text   => $output, -			format => 'text', -		); -	} -	else { -		my $station_name = $data->{station_name} // $station; -		$self->render( -			$template, -			departures       => \@departures, -			version          => $VERSION, -			title            => "Abfahrtsmonitor $station_name", -			refresh_interval => 120, -			hide_opts        => $hide_opts, -			hide_low_delay   => $hide_low_delay, -			show_realtime    => $show_realtime, -			load_marquee     => ( -				     $template eq 'single' -				  or $template eq 'multi' -			), -		); -	} -	return; -} - -get '/_redirect' => sub { -	my $self    = shift; -	my $station = $self->param('station'); -	my $params  = $self->req->params; - -	$params->remove('station'); - -	for my $param (qw(platforms backend mode admode via)) { -		if ( -			not $params->param($param) -			or ( exists $default{$param} -				and $params->param($param) eq $default{$param} ) -		  ) -		{ -			$params->remove($param); -		} -	} - -	$params = $params->to_string; - -	$self->redirect_to("/${station}?${params}"); -}; - -get '/_auto' => sub { -	my $self = shift; - -	$self->render( -		'geolocation', -		with_geolocation => 1, -		hide_opts        => 1 -	); -}; - -get '/_datenschutz' => sub { -	my $self = shift; - -	$self->render( 'privacy', hide_opts => 1 ); -}; - -post '/_geolocation' => sub { -	my $self = shift; - -	my $lon = $self->param('lon'); -	my $lat = $self->param('lat'); - -	if ( not $lon or not $lat ) { -		$self->render( json => { error => 'Invalid lon/lat received' } ); -	} -	else { -		my @candidates = map { -			{ -				ds100    => $_->[0][0], -				name     => $_->[0][1], -				eva      => $_->[0][2], -				lon      => $_->[0][3], -				lat      => $_->[0][4], -				distance => $_->[1], -			} -		} Travel::Status::DE::IRIS::Stations::get_station_by_location( $lon, -			$lat, 10 ); -		$self->render( -			json => { -				candidates => [@candidates], -			} -		); -	} -}; - -get '/_impressum' => sub { -	my $self = shift; - -	$self->render( 'imprint', hide_opts => 1 ); -}; - -get '/_wr/:train/:departure' => sub { -	my $self      = shift; -	my $train     = $self->stash('train'); -	my $departure = $self->stash('departure'); - -	my $wr = Travel::Status::DE::DBWagenreihung->new( -		departure    => $departure, -		train_number => $train, -	); - -	$self->render( -		'wagenreihung', -		wr        => $wr, -		hide_opts => 1, -	); -}; - -app->defaults( layout => 'default' ); -app->sessions->default_expiration( 3600 * 24 * 28 ); - -get '/'               => \&handle_request; -get '/multi/*station' => \&handle_request; -get '/*station'       => \&handle_request; - -app->config( -	hypnotoad => { -		accepts  => $ENV{DBFAKEDISPLAY_ACCEPTS} // 100, -		clients  => $ENV{DBFAKEDISPLAY_CLIENTS} // 10, -		listen   => [ $ENV{DBFAKEDISPLAY_LISTEN} // 'http://*:8092' ], -		pid_file => $ENV{DBFAKEDISPLAY_PID_FILE} // '/tmp/db-fakedisplay.pid', -		spare    => $ENV{DBFAKEDISPLAY_SPARE} // 2, -		workers  => $ENV{DBFAKEDISPLAY_WORKERS} // 2, -	}, -); - -app->types->type( json => 'application/json; charset=utf-8' ); -app->plugin('browser_detect'); -app->start(); +Mojolicious::Commands->start_app('DBInfoscreen');  | 
