Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
335 changes: 175 additions & 160 deletions youtube.pl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
########
# Required Modules:
# LWP::UserAgent
# HTML::TokeParser
# JSON::Parse
########
# Example:
# You type:
Expand All @@ -19,195 +19,208 @@
use warnings;
use Irssi;
use LWP::UserAgent;
use HTML::TokeParser;
use JSON::Parse 'parse_json';

use vars qw($VERSION %IRSSI);
$VERSION = "1.2";
$VERSION = "1.4";
%IRSSI = (
authors => "David O'Rourke",
contact => "phyber @ #irssi",
name => "youtube.pl",
description => "Add the title of a video to any youtube links you paste.",
license => "GPLv2",
changed => "2009/11/19",
authors => "David O'Rourke",
contact => "phyber @ #irssi",
name => "youtube.pl",
description => "Add the title of a video to any youtube links you paste.",
license => "GPLv2",
changed => "2020/02/06",
);

use constant {
INVALID => 0,
VALID_CHANNEL => 1,
VALID_QUERY => 2,
INVALID => 0,
VALID_CHANNEL => 1,
VALID_QUERY => 2,

YOUTUBE_PATTERN => qr/^(http(s?):\/\/)(www\.)?youtube\.com\/watch\?v=(.*)$/,
YOUTUBE_SHORTLINK => "http://youtu.be/",
YOUTUBE_PATTERN => qr/^(http(s?):\/\/)(www\.)?youtube\.com\/watch\?v=(.*)$/,
YOUTUBE_SHORTLINK => "http://youtu.be/",
};

# https://metacpan.org/pod/PHP::ParseStr
sub php_parse_str {
my ( $str ) = @_;

my $u = URI->new;
$u->query($str);

my %in = $u->query_form;
my $out = {};
while ( my ( $k, $v ) = each %in ) {
my $level = \$out;

my @parts = $k =~ /([^\[\]]+)/g;
foreach (@parts) {
if ( $_ =~ /^\d+$/ ) {
$$level //= [];
$level = \($$level->[$_]);
} else {
$$level //= {};
$level = \($$level->{$_});
}
}
$$level = $v;
}

return $out;
}
##
sub get_youtube_title {
my ($url) = @_;

# Get a user agent
my $ua = LWP::UserAgent->new;

# Set the UserAgent of the UserAgent
my $agent = Irssi::settings_get_str('youtube_useragent');
my $timeout = Irssi::settings_get_int('youtube_timeout');
$ua->agent($agent." ");
$ua->timeout($timeout);

# OK, now go and get the page and let the magic happen
my $response = $ua->get($url);
if (!$response->is_success) {
Irssi::print "Failed to fetch page for YouTube URL: $url";
return undef;
}

my $p = HTML::TokeParser->new(\$response->content);
if (!$p->get_tag("title")) {
Irssi::print "Failed to get title for YouTube URL: $url";
return undef;
}

# Strip youtube prefix/suffix.
my $title = $p->get_trimmed_text;
$title =~ s/YouTube\s- //;
$title =~ s/\s-\sYouTube//;
return $title;
my ($id) = @_;

my $ua = LWP::UserAgent->new;

my $agent = Irssi::settings_get_str('youtube_useragent');
my $timeout = Irssi::settings_get_int('youtube_timeout');
$ua->agent($agent." ");
$ua->timeout($timeout);

my $response = $ua->get("https://www.youtube.com/get_video_info?video_id=" . $id);
if (!$response->is_success) {
Irssi::print "Failed to fetch page for YouTube: $id";
return undef;
}

my $data = php_parse_str($response->content);
if (!$data->{player_response}) {
Irssi::print "Failed to read data from YouTube: $id";
return undef;
}
my $pr = $data->{player_response};
my $player = parse_json($pr);
my $vd = $player->{videoDetails};
my $title = $vd->{"title"};
my $author = $vd->{"author"};

return "$title ($author)";
}

sub is_valid_source {
my ($witem) = @_;
my ($witem) = @_;

if (!defined $witem) {
return INVALID;
}
if (!defined $witem) {
return INVALID;
}

my $wtype = $witem->{type};
my $wtype = $witem->{type};

return ($wtype eq "CHANNEL" or $wtype eq "QUERY");
return ($wtype eq "CHANNEL" or $wtype eq "QUERY");
}

sub is_valid_chan {
my ($wtype, $channel, $tag) = @_;

# First a quick check to see if this is a query and if we are enabled
# for ALL queries
if ($wtype eq "QUERY" and Irssi::settings_get_bool('youtube_queries')) {
return VALID_QUERY;
}

# Otherwise, check to see if we're on a valid channel.
foreach my $tc (split / /, Irssi::settings_get_str('youtube_channels')) {
my ($t, $c) = split /:/, $tc;

# We should always have at least $t, so lc it here.
$t = lc $t;

# Now we check if $c is defined. if it's not, we should have
# a nick or channel in $t
if (!defined $c) {
if ($t eq $channel) {
return VALID_CHANNEL;
}
}
else {
# lc $c here since it could be undefined above.
$c = lc $c;
if (($t eq $tag) and ($c eq $channel)) {
return VALID_CHANNEL;
}
}
}

return 0;
my ($wtype, $channel, $tag) = @_;

# First a quick check to see if this is a query and if we are enabled
# for ALL queries
if ($wtype eq "QUERY" and Irssi::settings_get_bool('youtube_queries')) {
return VALID_QUERY;
}

# Otherwise, check to see if we're on a valid channel.
foreach my $tc (split / /, Irssi::settings_get_str('youtube_channels')) {
my ($t, $c) = split /:/, $tc;

# We should always have at least $t, so lc it here.
$t = lc $t;

# Now we check if $c is defined. if it's not, we should have
# a nick or channel in $t
if (!defined $c) {
if ($t eq $channel) {
return VALID_CHANNEL;
}
}
else {
# lc $c here since it could be undefined above.
$c = lc $c;
if (($t eq $tag) and ($c eq $channel)) {
return VALID_CHANNEL;
}
}
}

return 0;
}

sub process_send_text {
my ($msg, $server_rec, $witem) = @_;
my $wtype = $witem->{type};

if (!defined $msg or !is_valid_source($witem)) {
return;
}

my $tag = lc $server_rec->{tag};
my $channel = lc $witem->{name};

# Check if we want to run in this tag and channel
if (!is_valid_chan($wtype, $channel, $tag)) {
return;
}

# Break the words out into an array and count the number of words.
my @words = split / /, $msg;
my $num_words = scalar @words;

# Loop over all of the words that we got, checking to see if any of
# them look like a youtube URL.
for (my $i = 0; $i < $num_words; $i++) {
# Grab the current word
my $w = $words[$i];

# Check it for signs of youtube.
my (undef, undef, undef, $vid) = $w =~ YOUTUBE_PATTERN;
if (!defined $vid) {
next;
}

# Attempt to get page title from youtube page.
my $title = get_youtube_title($w);
if (!defined $title) {
next;
}

# Check if we wanted to use a shortlink.
if (Irssi::settings_get_bool('youtube_shortlink')) {
($vid, my $discard) = split /\&/, $vid;
$w = YOUTUBE_SHORTLINK . $vid;
}

# If we got the title, also check if we wanted to make
# it a HD link
if (Irssi::settings_get_bool('youtube_hdlink')) {
if (!($w =~ m/fmt=18/)) {
$w = $w."&fmt=18";
}
}

# Overwrite the word in the array with our new
# youtube information.
my $new_text = "$w ($title)";
$words[$i] = $new_text;
}
Irssi::signal_continue((join(' ', @words), $server_rec, $witem));
my ($msg, $server_rec, $witem) = @_;
my $wtype = $witem->{type};

if (!defined $msg or !is_valid_source($witem)) {
return;
}

my $tag = lc $server_rec->{tag};
my $channel = lc $witem->{name};

# Check if we want to run in this tag and channel
if (!is_valid_chan($wtype, $channel, $tag)) {
return;
}

# Break the words out into an array and count the number of words.
my @words = split / /, $msg;
my $num_words = scalar @words;

# Loop over all of the words that we got, checking to see if any of
# them look like a youtube URL.
for (my $i = 0; $i < $num_words; $i++) {
# Grab the current word
my $w = $words[$i];

# Check it for signs of youtube.
my (undef, undef, undef, $vid) = $w =~ YOUTUBE_PATTERN;
if (!defined $vid) {
next;
}

# Attempt to get page title from youtube page.
my $title = get_youtube_title($vid);
if (!defined $title) {
next;
}

# Check if we wanted to use a shortlink.
if (Irssi::settings_get_bool('youtube_shortlink')) {
($vid, my $discard) = split /\&/, $vid;
$w = YOUTUBE_SHORTLINK . $vid;
}

# Overwrite the word in the array with our new
# youtube information.
my $new_text = "$w ($title)";
$words[$i] = $new_text;
}
Irssi::signal_continue((join(' ', @words), $server_rec, $witem));
}

sub usage {
# If we haven't got any channels set, print the usage.
if (Irssi::settings_get_str('youtube_channels') eq '') {
Irssi::print "youtube.pl v$VERSION";
Irssi::print "";
Irssi::print "CONFIG EXAMPLES";
Irssi::print "";
Irssi::print "Add channels to run in with:";
Irssi::print " /set youtube_channels tag:#channel";
Irssi::print "";
Irssi::print "The scripts useragent can be set with:";
Irssi::print " /set youtube_useragent SomeAgent/3.5";
Irssi::print "";
Irssi::print "Set the youtube timeout (in seconds) with:";
Irssi::print " /set youtube_timeout 3";
Irssi::print "";
Irssi::print "Toggle youtu.be shortlinks with:";
Irssi::print " /toggle youtube_shortlink";
Irssi::print "";
Irssi::print "Toggle automatically add HD to links with:";
Irssi::print " /toggle youtube_hdlink";
}
# If we haven't got any channels set, print the usage.
if (Irssi::settings_get_str('youtube_channels') eq '') {
Irssi::print "youtube.pl v$VERSION";
Irssi::print "";
Irssi::print "CONFIG EXAMPLES";
Irssi::print "";
Irssi::print "Add channels to run in with:";
Irssi::print " /set youtube_channels tag:#channel";
Irssi::print "";
Irssi::print "The scripts useragent can be set with:";
Irssi::print " /set youtube_useragent SomeAgent/3.5";
Irssi::print "";
Irssi::print "Set the youtube timeout (in seconds) with:";
Irssi::print " /set youtube_timeout 3";
Irssi::print "";
Irssi::print "Toggle youtu.be shortlinks with:";
Irssi::print " /toggle youtube_shortlink";
}
}
# Settings
Irssi::settings_add_str('youtube', 'youtube_useragent', 'Firefox/3.5');
Irssi::settings_add_str('youtube', 'youtube_channels', '');
Irssi::settings_add_bool('youtube', 'youtube_hdlink' => 1);
Irssi::settings_add_bool('youtube', 'youtube_queries' => 1);
Irssi::settings_add_bool('youtube', 'youtube_shortlink' => 1);
Irssi::settings_add_int('youtube', 'youtube_timeout', 3);
Expand All @@ -219,6 +232,8 @@ sub usage {
#####
# Version History
#####
## v1.4
# Patched to support Youtube in 2020 (<title> no longer contains video title)
## v1.3
# Many changes, same functionality.
#####
Expand Down