Add new gavinc plugins to MANIFEST.medium.
[matthijs/upstream/blosxom-plugins.git] / general / netflix
1 # Blosxom Plugin: netflix
2 # Author: Todd Larason (jtl@molehill.org)
3 # Version: 0+1i
4 # Blosxom Home/Docs/Licensing: http://blosxom.sourceforge.net/
5 # Netflix plugin Home/Docs/Licensing:
6 #   http://molelog.molehill.org/blox/Computers/Internet/Web/Blosxom/Netflix/
7 package netflix;
8
9 # -------------- Configuration Variables --------------
10
11 # Get this from your browser's cookie file.  I don't know (yet) how often it
12 # will need to be updated
13 $ShopperID     = undef
14   unless defined $ShopperID;
15
16 # how long to go between re-fetching the data, in seconds
17 # default value is 1 day
18 $max_cache_age = 60 * 60 * 24
19   unless defined $max_cache_age;
20
21 $debug_level   = 1
22   unless defined $debug_level;
23
24 # -----------------------------------------------------
25 \f
26 $have  = '';
27 $queue = '';
28 \f
29 use LWP::UserAgent;
30 use HTTP::Cookies;
31 use Storable;
32 use vars qw/$ShopperID $debug_level $max_cache_age $have $queue/;
33
34 use strict;
35 \f
36 my $cache;
37 my $package    = "netflix";
38 my $cachefile  = "$blosxom::plugin_state_dir/.$package.cache";
39 my $save_cache = 0;
40 my $url        = 'http://www.netflix.com/Queue?';
41 my $main_re    = qr!DVDs\sYou\sHave\sOut
42                    (.*)
43                    </table> .* DVDs\sin\sYour\sQueue
44                    (.*)
45                    </table>!xs;
46 my $item_re    = qr!href="http://www\.netflix\.com/MovieDisplay\?movieid=
47                     (\d+)
48                     &trkid=\d+">
49                     ([^<]+)
50                     </a>!xs;
51 \f
52 sub debug {
53     my ($level, @msg) = @_;
54
55     if ($debug_level >= $level) {
56         print STDERR "$package debug $level: @msg\n";
57     }
58 }
59
60 sub load_template {
61     my ($bit) = @_;
62     return $blosxom::template->('', "$package.$bit", $blosxom::flavour);
63 }
64
65 sub report {
66     my ($bit, $list, $id, $title) = @_;
67     my $f = load_template($bit);
68     $f =~ s/((\$[\w:]+)|(\$\{[\w:]+\}))/$1 . "||''"/gee;
69     return $f;
70 }
71 \f
72 sub prime_cache {
73     $cache = (-r $cachefile ? Storable::lock_retrieve($cachefile) : undef);
74     if ($cache && time - $cache->{timestamp} < $max_cache_age) {
75         debug(1, "Using cached state");
76         return 1;
77     }
78     $cache = {timestamp => time};
79     return 0;
80 }
81
82 sub save_cache {
83     return if (!$save_cache);
84     debug(1, "Saving cache");
85     Storable::lock_store($cache, $cachefile);
86 }
87 \f
88 sub get_page {
89     return $cache->{text} if defined $cache->{text};
90     my $jar = HTTP::Cookies->new;
91     my $req = HTTP::Request->new(GET => $url);
92     my $ua = LWP::UserAgent->new;
93     $jar->set_cookie(0, 'NetflixShopperId', $ShopperID, '/', '.netflix.com');
94     $jar->add_cookie_header($req);
95     my $res = $ua->request($req);
96     if (!$res->is_success) {
97         my $error = $res->status_line;
98         debug(0, "HTTP error: $error");
99         return undef;
100     }
101     $cache->{text} = $res->content;
102
103     # don't set $save_cache, because we only want to save the text
104     # if it's valid & parsable
105
106     return $cache->{text};
107 }
108
109 sub parse_page {
110     return if $#{$cache->{items}{have}}  >= 0;
111     return if $#{$cache->{items}{queue}} >= 0;
112     local ($_) = @_;
113     my ($out_text, $queue_text) = (m/$main_re/);
114     push @{$cache->{items}{have}}, [$1,$2] while ($out_text   =~ m/$item_re/g);
115     push @{$cache->{items}{queue}},[$1,$2] while ($queue_text =~ m/$item_re/g);
116 }
117
118 sub build_list {
119     my ($name, $items) = @_;
120     my $results;
121
122     return $cache->{list}{$name}{$blosxom::flavour}
123       if defined $cache->{list}{$name}{$blosxom::flavour};
124
125     $results  = report("head", "$name");
126     $results .= report("item", "$name", $_->[0], $_->[1]) foreach (@$items);
127     $results .= report("foot", "$name");
128
129     $cache->{list}{$name}{$blosxom::flavour} = $results;
130     $save_cache = 1 if $#{$items} >= 0;
131
132     return $results;
133 }
134 \f
135 sub start {
136     return 0 unless defined $netflix::ShopperID;
137     while (<DATA>) {
138         last if /^(__END__)?$/;
139         chomp;
140         my ($flavour, $comp, $txt) = split ' ',$_,3;
141         $txt =~ s:\\n:\n:g;
142         $blosxom::template{$flavour}{"$package.$comp"} = $txt;
143     }
144     return 1;
145 }
146
147 sub head {
148     prime_cache();
149     my $text = get_page();
150     return 0 unless defined $text;
151     parse_page($text);
152     $have  = build_list('have',  $cache->{items}{have});
153     $queue = build_list('queue', $cache->{items}{queue});
154     save_cache();
155 }
156
157 1;
158
159 __DATA__
160 error head <ul class="netflix $list">\n
161 error item <li><a href="http://www.netflix.com/MovieDisplay?movieid=$id">$title</a></li>\n
162 error foot </ul>\n
163 __END__
164
165 =head1 NAME
166
167 Blosxom Plug-in: netflix
168
169 =head1 SYNOPSIS
170
171 Purpose: Lets you easily share your Netflix queue information
172
173   * $netflix::have -- list of DVDs currently checked out (or on the way))
174   * $netflix::queue -- list of DVDs in your queue
175
176 =head1 VERSION
177
178 0+1i
179
180 1st test release
181
182 =head1 Customization
183
184 =head2 Configuration variables
185
186 C<$ShopperID> is the key to your Netflix identity; it's a cookie set by the
187 login process.  If your browser keeps its cookies in the historical Netscape
188 format, look in C<cookies.txt> for a line like:
189
190 .netflix.com    TRUE    /       FALSE   1050405980      NetflixShopperId        P0000000000000000000000445557579205
191
192 The long value starting with "P000" is the ShopperID.  Until you define this,
193 the plugin does nothing at all.
194
195 C<$max_cache_age> sets how long to cache the queue information for.
196
197 C<$debug_level> can be set to a value between 0 and 5; 0 will output
198 no debug information, while 5 will be very verbose.  The default is 1,
199 and should be changed after you've verified the plugin is working
200 correctly.
201
202 =head2 Classes for CSS control
203
204 There's are some classes used, available for CSS customization.
205
206   * C<netflix> -- both lists are in the netflix class
207   * C<have> -- the 'have' list is also in the have class
208   * C<queue> -- the 'queue' list is also in the queue class
209
210 =head2 Flavour-style files
211
212 If you want a format change that can't be made by CSS, you can
213 override the HTML generated by creating files similar to Blosxom's
214 flavour files.  They should be named netflix.I<bit>.I<flavour>; for
215 available I<bit>s and their default meanings, see the C<__DATA__>
216 section in the plugin.
217
218 =head1 Caching
219
220 Because fetching the queue information is relatively slow, I don't believe
221 anyone would want to use it without caching.  Thus, this module requires
222 Storable, and caching is always on.
223
224 =head1 BUGS
225
226 None known; please send bug reports and feedback to the Blosxom
227 development mailing list <blosxom-devel@lists.sourceforge.net>.
228
229 =head1 AUTHOR
230
231 Todd Larason  <jtl@molehill.org>, http://molelog.molehill.org/
232
233 This plugin is now maintained by the Blosxom Sourceforge Team,
234 <blosxom-devel@lists.sourceforge.net>.
235
236 =head1 LICENSE
237
238 this Blosxom Plug-in
239 Copyright 2003, Todd Larason
240
241 (This license is the same as Blosxom's)
242
243 Permission is hereby granted, free of charge, to any person obtaining a
244 copy of this software and associated documentation files (the "Software"),
245 to deal in the Software without restriction, including without limitation
246 the rights to use, copy, modify, merge, publish, distribute, sublicense,
247 and/or sell copies of the Software, and to permit persons to whom the
248 Software is furnished to do so, subject to the following conditions:
249
250 The above copyright notice and this permission notice shall be included
251 in all copies or substantial portions of the Software.
252
253 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
254 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
255 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
256 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
257 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
258 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
259 OTHER DEALINGS IN THE SOFTWARE.
260
261 =end