Plex Logs to Remote Syslog Server with rsyslog

Plex typically stores its log files here:
/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Logs/Plex Media Server.log

With rsyslog you can watch that file and forward lines from the log file.

apt-get update
apt-get -y install rsyslog
vi /etc/rsyslog.conf
module(load="imfile" PollingInterval="10")

# Plex Log File Monitor
input(type="imfile"
      File="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Logs/Plex Media Server.log"
      Tag="Plex Media Server:"
      Severity="info" # notice, info, warning...etc...
      Facility="local3")

Directly below that last line you can configure what and where to send the messages, here’s a few that I’m using:

if $syslogfacility-text == 'local3' and ($msg contains 'speed=0' or (($msg contains 'GET /video/') and ($msg contains 'Plex-Username'))) then @192.168.x.x
if $syslogfacility-text == 'local3' and ($msg contains 'stream will be transcoded') then @192.168.x.x

If you just want to send everything in the log file just use if $syslogfacility-text == 'local3' then @192.168.x.x

Scanning Plex Library for Missing TV Episodes

UPDATE: This is old and I’m guessing it doesn’t work anymore? I’m now all about PowerShell so I re-wrote it in PowerShell…. https://github.com/MysticRyuujin/PlexMissingEpisodes

The following Perl script will scan for every episode of all TV in your Plex server library (you have to specify what one) and then tell you what episodes you are missing for the shows in that library. It ignores Specials (S00Exx) and it ignores un-aired episodes.

You need to fill in the variables for your TheTVDB API Key, your Plex Server Name and Plex port (usually 32400). You also need to open your browser and go to:
http://$plexserver:$plexport/library/sections/

Then located the section number of the TV library you want to scan. Mine looks like this, and the $sectionid would be ‘2’:

<Directory allowSync="0" art="/:/resources/show-fanart.jpg" composite="/library/sections/2/composite/1439778056" filters="1" refreshing="1" thumb="/:/resources/show.png" key="2" type="show" title="TV Shows" agent="com.plexapp.agents.thetvdb" scanner="Plex Series Scanner" language="en" uuid="18a9bbb7-0a81-4b23-874b-1c433771efbd" updatedAt="1439778056" createdAt="1434676205">
<Location id="2" path="/media/FS01/TV"/>
use strict;
use warnings;
use XML::Simple;
use LWP::Simple;
use Archive::Zip;
use Archive::Zip::MemberRead;
use Encode qw( encode );
use DateTime;
use DateTime::Format::Strptime qw( );
use Sort::Naturally;

$|=1;
my $dt = DateTime->now;

my $apiKey = '';
my $plexserver = 'plex'; # IP works too
my $plexport = '32400';
my $sectionid = '2';

my ($xmlmirror, $bannermirror, $zipmirror) = &TVDB_GetMirrors();

my @plexseries = &PLEX_GetAllSeries();
my @plexguids;
foreach my $series (@plexseries)
{
	push(@plexguids, &PLEX_GetSeriesGUID($series));
}

my %myepisodes;

for (my $i = 0; $i < @plexguids; $i++)
{
	$myepisodes{$plexguids[$i]} = &PLEX_MyEpisodes($plexseries[$i]);
}

my %allepisodes;

for (my $i = 0; $i < @plexguids; $i++) { $allepisodes{$plexguids[$i]} = &TVDB_GetAllEpisodes($plexguids[$i]); } my %missing; foreach my $pkey (keys %allepisodes) { foreach my $key (keys $allepisodes{$pkey}) { if (!defined($myepisodes{$pkey}{$key})) { if ($key !~ /S00E/) { $missing{"$myepisodes{$pkey}{'title'} - $key"} = 1; } } } } foreach my $missing (nsort keys %missing) { print "$missingn"; } sub PLEX_MyEpisodes() { my $seriesid = $_[0]; my $dom = &getDom('http://'.$plexserver.':'.$plexport.'/library/metadata/'.$_[0].'/allLeaves'); my %episodes; $episodes{'title'} = $dom->{'parentTitle'};
	foreach my $key (keys $dom->{'Video'})
	{
		$episodes{sprintf ("S%02dE%02d", $dom->{'Video'}->{$key}->{'parentIndex'}, $dom->{'Video'}->{$key}->{'index'})} = 1;
	}
	return %episodes;
}

sub PLEX_GetSeriesGUID()
{
	my $dom = &getDom('http://'.$plexserver.':'.$plexport.'/library/metadata/'.$_[0].'/');
	my $guid = $1 if ($dom->{'Directory'}->{'/library/metadata/'.$_[0].'/children'}->{'guid'} =~ m#//(d+)?#);
	return $guid;
}

sub PLEX_GetAllSeries()
{
	my $dom = &getDom('http://'.$plexserver.':'.$plexport.'/library/sections/'.$sectionid.'/all/');
	my @series;
	foreach my $key (keys $dom->{'Directory'})
	{
		push(@series, $dom->{'Directory'}->{$key}->{'ratingKey'});
	}
	return @series;
}

sub getDom()
{
	my $url = $_[0];
	my $data = get($url);
	my $parser = new XML::Simple;
	return $parser->XMLin(encode("UTF-8", $data), ForceArray => 1);
}

sub TVDB_GetMirrors()
{
	my $mirrors_url = 'http://thetvdb.com/api/' . $apiKey . '/mirrors.xml';
	my $dom = &getDom($mirrors_url);
	
	my @xmlmirrors;
	my @bannermirrors;
	my @zipmirrors;

	foreach my $mirror (@{$dom->{'Mirror'}})
	{
		if ($mirror->{'typemask'}[0] & (1<<0)) { push(@xmlmirrors, $mirror->{'mirrorpath'}[0]);
		}
		if ($mirror->{'typemask'}[0] & (1<<1)) { push(@bannermirrors, $mirror->{'mirrorpath'}[0]);
		}
		if ($mirror->{'typemask'}[0] & (1<<2)) { push(@zipmirrors, $mirror->{'mirrorpath'}[0]);
		}
	}
	return ($xmlmirrors[rand(@xmlmirrors)], $bannermirrors[rand(@bannermirrors)], $zipmirrors[rand(@zipmirrors)]);
}

sub TVDB_GetAllEpisodes()
{
	my $seriesid = $_[0];
	my $url = $zipmirror . '/api/' . $apiKey . '/series/' . $seriesid . '/all/en.zip';
	my $zipname = "$seriesid" . '_en.zip';
	getstore($url, $seriesid . '_en.zip') unless ((-e $zipname) or (defined($_[1]) and ($_[1] eq 1)));
	my $zip = Archive::Zip->new();
	my $status = $zip->read($zipname);
	my $file = Archive::Zip::MemberRead->new($zip, "en.xml");
	my $xml;
	while (defined(my $line = $file->getline()))
	{
		$xml .= $line;
	}
	my $parser = new XML::Simple;
	my $dom = $parser->XMLin(encode("UTF-8", $xml), ForceArray => 1);
	
	my %episodes;
	my $format = DateTime::Format::Strptime->new(
		pattern   => '%Y-%m-%d',
		time_zone => 'local',
		on_error  => 'croak',
	);
	foreach my $episode (@{$dom->{'Episode'}})
	{
		$episode->{'FirstAired'}[0] = "3000-01-01" if (ref($episode->{'FirstAired'}[0]) eq "HASH");
		my $airdate = $format->parse_datetime($episode->{'FirstAired'}[0]);
		if (DateTime->compare($dt, $airdate) == 1)
		{
			$episodes{sprintf("S%02dE%02d", $episode->{'SeasonNumber'}[0], $episode->{'EpisodeNumber'}[0])} = 1;
		}
		
	}
	return %episodes;
}