File Coverage

blib/lib/WWW/FetchStory/Fetcher/PotionsAndSnitches.pm
Criterion Covered Total %
statement 9 118 7.6
branch 0 34 0.0
condition 0 3 0.0
subroutine 3 14 21.4
pod 11 11 100.0
total 23 180 12.7


line stmt bran cond sub pod time code
1             package WWW::FetchStory::Fetcher::PotionsAndSnitches;
2             $WWW::FetchStory::Fetcher::PotionsAndSnitches::VERSION = '0.2602';
3 1     1   162551 use strict;
  1         1  
  1         31  
4 1     1   3 use warnings;
  1         1  
  1         83  
5             =head1 NAME
6              
7             WWW::FetchStory::Fetcher::PotionsAndSnitches - fetching module for WWW::FetchStory
8              
9             =head1 VERSION
10              
11             version 0.2602
12              
13             =head1 DESCRIPTION
14              
15             This is the PotionsAndSnitches story-fetching plugin for WWW::FetchStory.
16              
17             =cut
18              
19 1     1   5 use parent qw(WWW::FetchStory::Fetcher);
  1         1  
  1         7  
20              
21             =head2 info
22              
23             Information about the fetcher.
24              
25             $info = $self->info();
26              
27             =cut
28              
29             sub info {
30 0     0 1   my $self = shift;
31            
32 0           my $info = "(http://www.potionsandsnitches.org) A Severus Snape + Harry Potter gen fiction archive.";
33              
34 0           return $info;
35             } # info
36              
37             =head2 priority
38              
39             The priority of this fetcher. Fetchers with higher priority
40             get tried first. This is useful where there may be a generic
41             fetcher for a particular site, and then a more specialized fetcher
42             for particular sections of a site. For example, there may be a
43             generic PotionsAndSnitches fetcher, and then refinements for particular
44             PotionsAndSnitches community, such as the sshg_exchange community.
45             This works as either a class function or a method.
46              
47             This must be overridden by the specific fetcher class.
48              
49             $priority = $self->priority();
50              
51             $priority = WWW::FetchStory::Fetcher::priority($class);
52              
53             =cut
54              
55             sub priority {
56 0     0 1   my $class = shift;
57              
58 0           return 1;
59             } # priority
60              
61             =head2 allow
62              
63             If this fetcher can be used for the given URL, then this returns
64             true.
65             This must be overridden by the specific fetcher class.
66              
67             if ($obj->allow($url))
68             {
69             ....
70             }
71              
72             =cut
73              
74             sub allow {
75 0     0 1   my $self = shift;
76 0           my $url = shift;
77              
78 0           return ($url =~ /potionsandsnitches/);
79             } # allow
80              
81             =head1 Private Methods
82              
83             =head2 extract_story
84              
85             Extract the story-content from the fetched content.
86              
87             my ($story, $title) = $self->extract_story(content=>$content,
88             title=>$title);
89              
90             =cut
91              
92             sub extract_story {
93 0     0 1   my $self = shift;
94 0           my %args = (
95             content=>'',
96             title=>'',
97             @_
98             );
99 0           my $content = $args{content};
100              
101 0           my $title = '';
102 0 0         if ($content =~ m#<title>([^<]+)</title>#s)
103             {
104 0           $title = $1;
105             }
106              
107 0           my $story='';
108 0 0         if ($content =~ m#(<div id="chaptertitle".*?)<div id="prev">#s)
    0          
109             {
110 0           $story = $1;
111             }
112             elsif ($content =~ m#(<div class=['"]chaptertitle['"].*?)<div class=['"]storyend['"]>#s)
113             {
114 0           $story = $1;
115             }
116 0 0 0       if ($story
117             and $story =~ m!<div style=[^>]+>The End.</div>!s)
118             {
119 0           $story =~ s!<div style=[^>]+>The End.</div>!!sg;
120             }
121              
122 0           my $para = '';
123 0 0         if ($content =~ m#(<div id="pagetitle">.*?)<div id="chaptertitle"#s)
    0          
124             {
125 0           $para = $1;
126             }
127             elsif ($content =~ m#(<div id="pagetitle">.*?)<div class=['"]chaptertitle['"]#s)
128             {
129 0           $para = $1;
130             }
131              
132 0           warn "title=$title\n";
133 0 0         if ($story)
134             {
135 0           $story = $self->tidy_chars($story);
136             }
137             else
138             {
139 0           return $self->SUPER::extract_story(%args);
140             }
141              
142 0           my $out = '';
143 0 0         $out .= $para if $para;
144 0           $out .= "$story\n";
145 0           return ($out, $title);
146             } # extract_story
147              
148             =head2 make_css
149              
150             Create site-specific CSS styling.
151              
152             $css = $self->make_css();
153              
154             =cut
155              
156             sub make_css {
157 0     0 1   my $self = shift;
158              
159 0           my $out = '';
160 0           $out .= <<EOT;
161             <style type="text/css">
162             hr {
163             width: 70%;
164             color: #000;
165             background: #000;
166             margin: 1em auto;
167             }
168             .infobox {
169             border: 1px solid #336;
170             margin: 1em 10%;
171             padding: 1em;
172             }
173             #pagetitle {
174             font-size: 140%;
175             text-align: center;
176             font-weight: bold;
177             margin-bottom: 1ex;
178             }
179             #pagetitle A {
180             color: #336;
181             font-weight: bold;
182             text-decoration: none;
183             }
184             .label { font-weight: bold; }
185             .chaptertitle {
186             font-size: 125%;
187             text-align: center;
188             text-decoration: underline;
189             margin-bottom: 1em;
190             }
191             .notes {
192             border: 1px solid #336;
193             margin: 1em 10%;
194             }
195             .notes .title {
196             padding: 5px;
197             border-bottom: 1px solid #336;
198             }
199             .notes .noteinfo { padding: 5px; }
200             #copyright {
201             margin: 1em 10%;
202             border: 1px solid #336;
203             padding: 1ex;
204             }
205             #archivedat { text-align: center; }
206             .toplink { text-align: right; }
207             .toplink A {
208             font-size: 80%;
209             color #336;
210             font-weight: bold;
211             }
212             </style>
213             EOT
214 0           return $out;
215             } # make_css
216              
217             =head2 parse_toc
218              
219             Parse the table-of-contents file.
220              
221             %info = $self->parse_toc(content=>$content,
222             url=>$url,
223             urls=>\@urls);
224              
225             This should return a hash containing:
226              
227             =over
228              
229             =item chapters
230              
231             An array of URLs for the chapters of the story. In the case where the
232             story only takes one page, that will be the chapter.
233             In the case where multiple URLs have been passed in, it will be those URLs.
234              
235             =item title
236              
237             The title of the story.
238              
239             =back
240              
241             It may also return additional information, such as Summary.
242              
243             =cut
244              
245             sub parse_toc {
246 0     0 1   my $self = shift;
247 0           my %args = (
248             url=>'',
249             content=>'',
250             @_
251             );
252              
253 0           my %info = ();
254 0           my $content = $args{content};
255              
256 0           my @chapters = ();
257 0           $info{url} = $args{url};
258 0           my $sid='';
259 0 0         if ($args{url} =~ m#sid=(\d+)#)
260             {
261 0           $sid = $1;
262             }
263             else
264             {
265 0           return $self->SUPER::parse_toc(%args);
266             }
267 0           $info{title} = $self->parse_title(%args);
268 0           $info{author} = $self->parse_author(%args);
269 0           $info{summary} = $self->parse_summary(%args);
270 0           $info{characters} = $self->parse_characters(%args);
271 0           $info{universe} = 'Harry Potter';
272 0           $info{chapters} = $self->parse_chapter_urls(%args, sid=>$sid);
273              
274 0           return %info;
275             } # parse_toc
276              
277             =head2 parse_chapter_urls
278              
279             Figure out the URLs for the chapters of this story.
280              
281             =cut
282             sub parse_chapter_urls {
283 0     0 1   my $self = shift;
284 0           my %args = (
285             url=>'',
286             content=>'',
287             @_
288             );
289 0           my $content = $args{content};
290 0           my $sid = $args{sid};
291 0           my @chapters = ();
292 0 0         if (defined $args{urls})
293             {
294 0           @chapters = @{$args{urls}};
  0            
295             }
296 0 0         if (@chapters == 1)
297             {
298             # fortunately Potions-And-Snitches has a sane chapter system
299 0 0         if ($content =~ m#<span class="label">Chapters:\s*</span>\s*(\d+)#s)
300             {
301 0           @chapters = ();
302 0           my $fmt = 'http://www.potionsandsnitches.org/fanfiction/viewstory.php?action=printable&textsize=0&sid=%d&chapter=%d';
303 0           my $num_ch = $1;
304 0           for (my $i=1; $i <= $num_ch; $i++)
305             {
306 0           my $ch_url = sprintf($fmt, $sid, $i);
307 0 0         warn "chapter=$ch_url\n" if ($self->{verbose} > 1);
308 0           push @chapters, $ch_url;
309             }
310             }
311             }
312              
313 0           return \@chapters;
314             } # parse_chapter_urls
315              
316             =head2 parse_title
317              
318             Get the title from the content
319              
320             =cut
321             sub parse_title {
322 0     0 1   my $self = shift;
323 0           my %args = (
324             url=>'',
325             content=>'',
326             @_
327             );
328              
329 0           my $content = $args{content};
330 0           my $title = '';
331 0 0         if ($content =~ m#<div id="pagetitle"><a href="viewstory.php\?sid=\d+">([^<]+)</a>#s)
332             {
333 0           $title = $1;
334             }
335             else
336             {
337 0           $title = $self->SUPER::parse_title(%args);
338             }
339 0           return $title;
340             } # parse_title
341              
342             =head2 parse_author
343              
344             Get the author from the content
345              
346             =cut
347             sub parse_author {
348 0     0 1   my $self = shift;
349 0           my %args = (
350             url=>'',
351             content=>'',
352             @_
353             );
354              
355 0           my $content = $args{content};
356 0           my $author = '';
357 0 0         if ($content =~ m#\s*by\s*<a href="viewuser.php\?uid=\d+">([^<]+)</a></div>#s)
358             {
359 0           $author = $1;
360             }
361             else
362             {
363 0           $author = $self->SUPER::parse_author(%args);
364             }
365 0           return $author;
366             } # parse_author
367              
368             =head2 parse_summary
369              
370             Get the summary from the content
371              
372             =cut
373             sub parse_summary {
374 0     0 1   my $self = shift;
375 0           my %args = (
376             url=>'',
377             content=>'',
378             @_
379             );
380              
381 0           my $content = $args{content};
382 0           my $summary = '';
383 0 0         if ($content =~ m#<span class="label">Summary:</span>\s*(.*?)\s*<br><br>#s)
384             {
385 0           $summary = $1;
386             }
387             else
388             {
389 0           $summary = $self->SUPER::parse_summary(%args);
390             }
391 0           return $summary;
392             } # parse_summary
393              
394             =head2 parse_characters
395              
396             Get the characters from the content
397              
398             =cut
399             sub parse_characters {
400 0     0 1   my $self = shift;
401 0           my %args = (
402             url=>'',
403             content=>'',
404             @_
405             );
406              
407 0           my $content = $args{content};
408 0           my $characters = '';
409 0 0         if ($content =~ m#<span class="label">Characters:\s*</span>(.*?)<td#s)
410             {
411 0           my $chars_str = $1;
412 0           my @chars = ();
413 0           while ($chars_str =~ m#>([^<]+)</a>#sg)
414             {
415 0           my $character = $1;
416 0           $character =~ s/!Snape and Harry \(required\)/Harry Potter, Severus Snape/;
417 0           push @chars, $character;
418             }
419 0           $characters = join(', ', @chars);
420             }
421             else
422             {
423 0           $characters = $self->SUPER::parse_characters(%args);
424             }
425 0           return $characters;
426             } # parse_characters
427              
428             1; # End of WWW::FetchStory::Fetcher::PotionsAndSnitches
429             __END__