MythTV/0000775000076500007650000000000010726775346011662 5ustar robertrobertMythTV/Suggest.pm0000664000076500007650000002266310726775346013652 0ustar robertrobert################################################################################ # $Id: Suggest.pm 1543 2007-12-09 14:25:40Z robert $ ################################################################################ # # Revision : $Rev: 1543 $ # Last Changed: $Date: 2007-12-09 15:25:40 +0100 (Sun, 09 Dec 2007) $ ################################################################################ package MythTV::Suggest; use strict; use warnings; use Mysql; use IO::File; use AI::Categorizer::Document; use AI::Categorizer::Category; use AI::Categorizer::KnowledgeSet; use AI::Categorizer::Learner::NaiveBayes; my $ERROR; ### constructor sub new { my $proto = shift; my $class = ref $proto || $proto; my $self = bless { @_ }, $class; $self->_initialize or return undef; return $self; } sub _error { my ($self, $msg) = @_; $ERROR = $msg; return undef; } sub error { return $ERROR; } sub verbose { my ($self, $msg) = @_; print STDERR "$msg\n" if $self->{verbose}; } sub _initialize { my $self = shift; $self->{verbose} ||= 0; # setup database $self->verbose("connecting to database"); $self->{_db} = Mysql->connect( $self->{hostname}, $self->{database}, $self->{username}, $self->{password} ) or return $self->_error(Mysql->errmsg()); # setup categorizer $self->verbose("setting up categorizer"); $self->{_c} = AI::Categorizer::Learner::NaiveBayes->new(verbose => $self->{verbose}); return 1; } sub learn { my ($self, $statefile) = @_; my $db = $self->{_db}; my $c = $self->{_c}; my $k = AI::Categorizer::KnowledgeSet->new( verbose => $self->{verbose}, features_kept => 0.5, tfidf_weighting => 'nfc', ); $statefile ||= $self->{statefile}; # setup category-objects my $goodCat = new AI::Categorizer::Category(name => 'good'); my $badCat = new AI::Categorizer::Category(name => 'bad'); # first, select all known shows and learn those as 'bad' $self->verbose("learning 'bad' shows"); $self->_learn($db->query("SELECT title, subtitle, description FROM program"), $k, $badCat); # select all previously recorded shows and learn those as 'good' $self->verbose("learning 'good' shows"); $self->_learn($db->query("SELECT title, subtitle, description FROM oldrecorded"), $k, $goodCat); # also select all currently recorded shows and learn those as 'good' $self->verbose("learning more 'good' shows"); $self->_learn($db->query("SELECT title, subtitle, description FROM recorded"), $k, $goodCat); # crunch numbers $self->verbose("training knowledgeset"); $c->train(knowledge_set => $k); # save statefile $self->verbose("saving state to file"); $c->save_state($statefile); } # helperfunction sub _learn { my ($self, $sth, $k, $cat) = @_; my $count = 0; while (my @row = $sth->fetchrow_array) { my $content = sprintf "%s %s %s", $row[0], $row[1], $row[2]; $k->add_document(new AI::Categorizer::Document( name => key($content), content => $content, categories => [ $cat ], stopwords => $self->{stopwords} || {}, )); $count++; } $self->verbose("...learned $count documents"); } sub suggest { my ($self, $statefile) = @_; my $db = $self->{_db}; $statefile ||= $self->{statefile}; # load statefile $self->verbose("loading state from file"); my $c = AI::Categorizer::Learner::NaiveBayes->restore_state($statefile); # categorize all non-recorded shows to make suggestions (TODO: skip # to-be-recorded shows) $self->verbose("quering database for new shows"); my $sth = $db->query(<= NOW() EOQ $self->verbose("...retrieved information for " . $sth->numrows . " shows from database"); # process each result $self->verbose("categorizing each show"); my $results = []; while (my @row = $sth->fetchrow_array) { my $content = sprintf "%s %s %s", $row[0], $row[1], $row[2]; # TODO: skip already-learned shows? they probably don't have # a timestamp starting in the future so this might be moot # categorize this show my $res = $c->categorize(new AI::Categorizer::Document(content => $content)); if ($res->in_category('good')) { my @scores = $res->scores($res->categories); push @$results, { title => $row[0], subtitle => $row[1], description => $row[2], starttime => $row[3], channel => $row[4], chanid => $row[5], timestamp => $row[6], score => $scores[0], }; } } $self->verbose("done categorizing"); # sort and return results return [ sort { $b->{score} <=> $a->{score} } @$results ]; } # helper: turn $content into a unique key sub key { my $content = shift; # no-op for now return $content; } 1; __END__ =head1 NAME MythTV::Suggest - TiVo-like suggestions for MythTV =head1 SYNOPSIS use MythTV::Suggest; my $my = MythTV::Suggest->new( hostname => $hostname, database => $database, username => $username, password => $password, statefile => $statefile, stopwords => Lingua::StopWords::EN::getStopWords(), ) or die MythTV::Suggest->error; # learning mode? if ($learn) { $my->learn(); } else { my $suggestions = $my->suggest(); # show them for my $show (@$suggestions) { print <{score} ]} TITLE = $show->{title} SUBTITLE = $show->{subtitle} DESCRIPTION = $show->{description} STARTTIME = $show->{starttime} CHANNEL = $show->{channel} LINK = http://my.mythtv.box.local/mythweb/tv/detail/$show->{chanid}/$show->{timestamp} ---------- EOB } } =head1 DESCRIPTION This package can be used to build a TiVo-like suggestion system for MythTV. It operates in one of two modes: L or L. =head2 Learning mode In learning mode, MythTV::Suggest builds (using L) a Bayesian network of TV-programs contained in your MythTV database. Programs default to I (not interesting), unless they have previously been recorded, in which case they are marked I (interesting). After all programs have been considered, the Bayesian network is written to B<$statefile>, which will be used in L. =head2 Suggestion mode After having learned your preferences, you can use MythTV::Suggest to suggest new programs which you might like. Upcoming TV-programs which categorize as I will be suggested. =head1 METHODS =over 4 =item B Returns a new instance of the MythTV::Suggest class. Options are: =over 8 =item I (mandatory) The name of the host where your MythTV MySQL-database is located =item I (mandatory) The name of the MythTV database (usually I) =item I (mandatory) Username to connect to the database with (usually I) =item I (mandatory) Password to connect to the database with (usually I as well) =item I (optional) Full pathname of file where the Bayesian network will be stored (in L) or read from (in L). =item I (optional) An hash of words not to consider when building the Bayesian network. =back =item B Initiate L by throwing away all previously learned data and relearning your preferences from the MythTV database. The optional argument B<$statefile> overrides the B argument of the constructor. =item B Initiate L by loading previously learned data from the statefile and considering all shows in the MythTV database. Note that only shows which are still to be aired are suggested! Returns a sorted (by score, highest score first) array with suggestions. Each array-item contains a hash-reference with the following schema: =over 8 =item I Title of the suggested TV-show =item I<subtitle> Subtitle (rather: episode-name) of the suggested TV-show =item I<description> Description of the suggested episode =item I<starttime> Starttime of the suggested episode =item I<channel> Name of channel of the suggested episode =item I<chanid> Channel-ID (useful if you want to link to the MythWeb page for this episode) of the suggested episode =item I<timestamp> Unix-timestamp (also useful if you want to link to the MythWeb page) of the suggested episode =item I<score> The score of the suggested episode; higher is better =back As before, the optional argument B<$statefile> overrides the B<statefile> argument of the constructor. =back =head1 PREREQUISITES These are the modules I used to build this module: =over 4 =item * L<Mysql> (1.2401) =item * L<AI::Categorizer> =back I've tested this module on my MythTV-backend running version 0.20 and 0.20.2. =head1 TODO The biggest TODO: improve speed! I changed from using L<AI::Categorize::NaiveBayes> to L<AI::Categorizer::Learner::NaiveBayes> because the former seems to be discontinued. However, it was *much* faster than the latter. Perhaps I'm overlooking something though, so I have to research it some more. Another TODO: don't suggest episodes which are already marked as I<will be recorded>. However, the internals of the MythTV database are quite complex and I haven't yet figured out how to do this properly (especially with shows with re-occurring recordingschedules). =head1 SEE ALSO L<http://mythtv.org> =head1 AUTHOR Robert Klep, L<robert AT klep DOT name> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������