#!/usr/bin/env perl
# Ping M3U playlist items

use v5.12;
use strict;
use warnings;

our $VERSION = "1.20";

use URI;
use MP3::M3U::Parser;
use Getopt::Std;
use Net::Ping;
use LWP::UserAgent;

our %opts;
getopts("n:o", \%opts)
    or die("Error in command-line-arguments: $!");

# FIX: Prevent SIGPIPE (exit 141)
$SIG{PIPE} = 'IGNORE';

sub ping_echo_host {
    my ($uri) = @_;
    my $host = $uri->host or return;
    my $port = $uri->port || $uri->default_port || 80;

    my $p = Net::Ping->new("tcp");
    $p->port_number($port);

    my $ok = $p->ping($host);
    $p->close;

    return $ok;
}

sub ping_http_head {
    my ($uri) = @_;

    my $ua = LWP::UserAgent->new(
        timeout => 10,
        agent   => "PingM3U/1.10",
    );

    my $res = eval { $ua->head($uri) };
    if ($@) {
        warn "$uri: $@";
        return;
    }

    return $res && $res->is_success;
}

my ($offline, $online, $double) = (0, 0, 0);
my @streams;
my %seen;

for my $file (@ARGV) {

    my $parser = MP3::M3U::Parser->new();
    $parser->parse($file);

    for my $entry (@{ $parser->result }) {
        for my $row (@{ $entry->{data} }) {

            my ($url, $name, $duration) = @$row;
            next unless $url && $name && $duration;

            my $uri = URI->new($url);
            next unless $uri;

            # PING
            if (!ping_http_head($uri)) {
                say STDERR "Offline\t$url ($name)";
                $offline++;
                next;
            }

            # DUPLICATE CHECK
            if ($seen{$url}++) {
                say STDERR "Double\t$url ($name)" unless $opts{o};
                $double++;
                next;
            }

            say STDERR "Online\t$url ($name)" unless $opts{o};
            $online++;
            push @streams, $row;
        }
    }
}

# WRITE NEW M3U FILE
if ($opts{n} && @streams) {
    open my $fh, '>:encoding(UTF-8)', $opts{n}
        or die "Cannot write to $opts{n}: $!";

    say $fh "#EXTM3U";

    for my $s (@streams) {
        my ($url, $name, $duration) = @$s;
        say $fh "#EXTINF:$duration,$name";
        say $fh $url;
    }

    close $fh;
}

# STATISTICS
my $total = $online + $offline + $double;
say STDERR "Total:\t$total";

if ($total > 0) {
    say STDERR "Online:\t$online\t",  sprintf("%.2f%%", 100 * $online  / $total);
    say STDERR "Offline:\t$offline\t", sprintf("%.2f%%", 100 * $offline / $total);
    say STDERR "Double:\t$double\t",   sprintf("%.2f%%", 100 * $double  / $total);
}

exit !scalar(@streams);
