Skip to content

Instantly share code, notes, and snippets.

@gfldex
Created September 28, 2017 10:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gfldex/000b2c3e8da1c3f77837ef5f643a1ecd to your computer and use it in GitHub Desktop.
Save gfldex/000b2c3e8da1c3f77837ef5f643a1ecd to your computer and use it in GitHub Desktop.
#! /usr/bin/env perl6
use v6.c;
use WWW::RadioBrowser;
say 'start';
my &GREEN = $*OUT.t ?? sub (**@s) { "\e[32m{@s.join('')}\e[0m" } !! sub (|c) { c };
my &RED = $*OUT.t ?? sub (**@s) { "\e[31m{@s.join('')}\e[0m" } !! sub (|c) { c };
# my @stations = internet-radio-stations-update;
# say +@stations;
say GREEN internet-radio-stations-state;
react {
whenever internet-radio-update-progress {
say GREEN .Str;
when storing {
say GREEN internet-radio-stations-state;
my $counter;
$counter++ for internet-radio-stations();
say $counter;
# {
# for .item.kv -> $k, $v {
# # say $k, ‚: ‘, $v, ‚ ‘, $v.WHAT;
# }
# }
}
when ready {
whenever Promise.in(2) { done }
}
}
whenever radio-browser-debug {
say RED .Str;
}
}
sub dump-structure(\v, $level is copy = 0){
say " " x $level, v.WHAT;
$level++;
v».&dump-structure($level) if v ~~ Iterable;
}
# dump-structure internet-radio-stations-deleted[0];
use v6.c;
use WWW;
use JSON::Fast;
# my &note = $*OUT.t ?? sub (**@s) { $*ERR.put: "{now.DateTime.Str} \e[1m{@s.join('')}\e[0m" } !! sub (|c) { $*ERR.say: c };
constant term:<GMT> = :timezone(0);
my $cfg-dir = „$*HOME/.config“.IO.e ?? „$*HOME/.config/perl6-www-radiobrowser“ !! „$*HOME/.perl6-www-radiobrowser“;
mkdir $cfg-dir unless $cfg-dir.IO.e;
enum InternetRadioStationsState is export <setup fetching reading processing storing ready error>;
constant internet-radio-update-progress is export = Supplier.new;
constant radio-browser-debug is export = Supplier.new;
# my &note = sub (**@s){ radio-browser-debug.emit: DateTime.now.hh-mm-ss ~ ': ' ~ @s.join }
my @cache;
my $cache-state = setup;
start {
@cache = internet-radio-stations-update();
}
sub internet-radio-stations() is export {
@cache
}
sub internet-radio-stations-state() is export {
$cache-state;
}
sub change-state(InternetRadioStationsState $s){
$cache-state = $s;
internet-radio-update-progress.emit: $s
}
sub ISO8601(DateTime:D $dt) {
sub zPad(Str(Any) $s, int $p){'0' x $p - $s.chars ~ $s}
( 0 > $dt.year ?? '-'~ zPad( $dt.year.abs, 4 ) !!
$dt.year <= 9999 ?? zPad( $dt.year, 4 )
!! '+'~ zPad( $dt.year, 5 ) ) ~'-'~
zPad( $dt.month, 2 ) ~'-'~ zPad( $dt.day, 2 ) ~'T'~
zPad( $dt.hour, 2 ) ~':'~ zPad( $dt.minute, 2 ) ~':'~
( $dt.second.floor == $dt.second
?? zPad( $dt.second.Int, 2 )
!! $dt.second.fmt('%09.6f') )
~
( $dt.timezone == 0
?? 'Z'
!! $dt.timezone > 0
?? ( '+' ~ zPad( ($dt.timezone/3600).floor, 2 ) ~':'~
zPad( ($dt.timezone/60%60).floor, 2 ) )
!! ( '-' ~ zPad( ($dt.timezone.abs/3600).floor, 2 ) ~':'~
zPad( ($dt.timezone.abs/60%60).floor, 2 ) ) )
}
my %type-map =
lastchangetime => DateTime,
clickcount => Int,
bitrate => Int,
clicktimestamp => DateTime,
votes => Int,
lastcheckoktime => DateTime,
clicktrend => Int,
lastcheckok => Bool,
negativevotes => Int,
tage => { .split(',') },
lastchecktime => DateTime,
id => Int;
sub internet-radio-stations-update(:$schema = 'http', Int :$limit = 100000) is export {
my $cache-time = (try „$cfg-dir/stations.json“.IO.modified.DateTime) // now.DateTime.new(0);
my @stations;
if $cache-time < now.DateTime.earlier(:30days) {
change-state fetching;
note "fetching all unbroken stations";
@stations = | jpost "$schema://www.radio-browser.info/webservice/json/stations", {User-Agent => 'https://github.com/gfldex/perl6-www-radiobrowser'}, :$limit;
my $stations-count = +@stations;
change-state processing;
note "processing $stations-count stations";
@stations.=map: {
.<lastchangetime> = .<lastchangetime> ?? DateTime.new(.<lastchangetime>.subst(' ', 'T') ~ 'Z', :formatter(&ISO8601)) !! DateTime;
.<clickcount> = .<clickcount>.Int;
.<bitrate> = .<bitrate>.Int;
.<clicktimestamp> = .<clicktimestamp> ?? DateTime.new(.<clicktimestamp>.subst(' ', 'T') ~ 'Z', :formatter(&ISO8601)) !! DateTime;
.<votes> = .<votes>.Int;
.<lastcheckoktime> = .<lastcheckoktime> ?? DateTime.new(.<lastcheckoktime>.subst(' ', 'T') ~ 'Z', :formatter(&ISO8601)) !! DateTime;
.<clicktrend> = .<clicktrend>.Int;
.<lastcheckok> = .<lastcheckok>.Int.Bool;
.<negativevotes> = .<negativevotes>.Int;
.<tags> = .<tags>.split(',');
.<lastchecktime> = .<lastchecktime> ?? DateTime.new(.<lastchecktime>.subst(' ', 'T') ~ 'Z', :formatter(&ISO8601)) !! DateTime;
.<id> = .<id>.Int;
(note "$_/$stations-count processed" if $_ %% 1000) with $++;
.Hash
};
@cache = @stations;
change-state storing;
{
„$cfg-dir/stations.json“.IO.spurt(@stations.&to-json);
my $elapsed = now - ENTER now;
note "$stations-count stored in {$elapsed}s with {$stations-count / $elapsed} stations/s";
}
change-state ready;
note ‚cache filled in: ‘ ~ (now - ENTER now) ~ ‚s‘;
} else {
change-state reading;
note "read cache from $cfg-dir/stations.json";
my @stations = @(„$cfg-dir/stations.json“.IO.slurp.&from-json);
my $stations-count = @stations.elems;
{
change-state processing;
note "processing $stations-count stations";
@stations.=map: {
.AT-KEY(‚lastchangetime‘) = .AT-KEY(‚lastchangetime‘) ?? DateTime.new(.AT-KEY(‚lastchangetime‘), :formatter(&ISO8601)) !! DateTime;
.AT-KEY(‚clickcount‘) = .AT-KEY(‚clickcount‘).Int;
.AT-KEY(‚bitrate‘) = .AT-KEY(‚bitrate‘).Int;
.AT-KEY(‚clicktimestamp‘) = .AT-KEY(‚clicktimestamp‘) ?? DateTime.new(.AT-KEY(‚clicktimestamp‘), :formatter(&ISO8601)) !! DateTime;
.AT-KEY(‚votes‘) = .AT-KEY(‚votes‘).Int;
.AT-KEY(‚lastcheckoktime‘) = .AT-KEY(‚lastcheckoktime‘) ?? DateTime.new(.AT-KEY(‚lastcheckoktime‘), :formatter(&ISO8601)) !! DateTime;
.AT-KEY(‚clicktrend‘) = .AT-KEY(‚clicktrend‘).Int;
.AT-KEY(‚lastcheckok‘) = .AT-KEY(‚lastcheckok‘).Int.Bool;
.AT-KEY(‚negativevotes‘) = .AT-KEY(‚negativevotes‘).Int;
#.AT-KEY(‚tags‘) = .AT-KEY(‚tags‘).split(',');
.AT-KEY(‚lastchecktime‘) = .AT-KEY(‚lastchecktime‘) ?? DateTime.new(.AT-KEY(‚lastchecktime‘), :formatter(&ISO8601)) !! DateTime;
.AT-KEY(‚id‘) = .AT-KEY(‚id‘).Int;
(note "$_/$stations-count processed" if $_ %% 1000) with $++;
.Hash
};
my $elapsed = now - ENTER now;
note "$stations-count processed in {$elapsed}s with {$stations-count / $elapsed} stations/s";
}
my @deleted-stations = internet-radio-stations-deleted(since => $cache-time);
my @updated-stations = internet-radio-stations-changed(since => $cache-time);
note [ ‚total:‘, +@stations, ‚deleted:‘, +@deleted-stations, ‚changed:‘, +@updated-stations ];
if +@deleted-stations | +@updated-stations {
my Set $d-s-ids = @deleted-stations».<id>.Set;
my Set $u-s-ids = @updated-stations».<id>.Set;
my Set $to-be-removed-stations = $d-s-ids ∩ $u-s-ids;
@stations = @stations.grep({
my $station = .item;
my $ret = .<id>.Int ∉ $to-be-removed-stations;
CATCH { default {
note .^name, ': ', .Str;
exit 0
}
}
$ret
});
note ‚after delete: ‘, +@stations;
# dd @stations.grep({say .<id>; .<id>.Int ∈ $to-be-removed-stations});
# dd $to-be-removed-stations;
@stations.append(|@updated-stations);
note ‚after updated: ‘, +@stations;
change-state storing;
{
„$cfg-dir/stations.json“.IO.spurt(@stations.&to-json);
my $elapsed = now - ENTER now;
note "$stations-count stored in {$elapsed}s with {$stations-count / $elapsed} stations/s";
}
}
change-state ready;
note ‚cache updated in: ‘ ~ (now - ENTER now) ~ ‚s‘;
}
@stations.Slip
}
multi sub internet-radio-stations-deleted(:$schema = 'http', DateTime :$since = now.DateTime.earlier(:30days)) is export {
note "fetching deleted stations";
my \v = jpost "$schema://www.radio-browser.info/webservice/json/stations/deleted", {User-Agent => 'https://github.com/gfldex/perl6-www-radiobrowser'}
v.map: {
.<lastchangetime> = DateTime.new: .<lastchangetime>.subst(' ', 'T') ~ 'Z';
.<changeid> = .<changeid>.Int;
.<id> = .<changeid>.Int;
.Hash
};
v.grep: { .<lastchangetime> > $since }
}
multi sub internet-radio-stations-deleted(:$schema = 'http', :$age) is export {
internet-radio-stations-deleted(:$schema, since => now.DateTime.earlier(|$age))
}
multi sub internet-radio-stations-changed(:$schema = 'http', DateTime :$since) is export {
note "fetching changed stations";
my \v = jpost "$schema://www.radio-browser.info/webservice/json/stations/lastchange", {User-Agent => 'https://github.com/gfldex/perl6-www-radiobrowser'};
v.map: {
.<lastchangetime> = .<lastchangetime> ?? DateTime.new: .<lastchangetime>.subst(' ', 'T') ~ 'Z' !! DateTime;
.<clickcount> = .<clickcount>.Int;
.<bitrate> = .<bitrate>.Int;
.<clicktimestamp> = .<clicktimestamp> ?? DateTime.new: .<clicktimestamp>.subst(' ', 'T') ~ 'Z' !! DateTime;
.<votes> = .<votes>.Int;
.<lastcheckoktime> = .<lastcheckoktime> ?? DateTime.new: .<lastcheckoktime>.subst(' ', 'T') ~ 'Z' !! DateTime;
.<clicktrend> = .<clicktrend>.Int;
.<lastcheckok> = .<lastcheckok>.Int.Bool;
.<negativevotes> = .<negativevotes>.Int;
.<tags> = .<tags>.split(',');
.<lastchecktime> = .<lastchangetime> ?? DateTime.new: .<lastchecktime>.subst(' ', 'T') ~ 'Z' !! DateTime;
.<id> = .<id>.Int;
.Hash
};
v.grep: { .<lastchangetime> > $since }
}
multi sub internet-radio-stations-changed(:$schema = 'http', :$age) is export {
internet-radio-stations-changed(:$schema, since => now.DateTime.earlier(|$age))
}
multi sub fetch(:$stations-changed){
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment