File Coverage

blib/lib/StreamFinder.pm
Criterion Covered Total %
statement 9 138 6.5
branch 0 116 0.0
condition 0 84 0.0
subroutine 3 4 75.0
pod 1 1 100.0
total 13 343 3.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             StreamFinder - Fetch actual raw streamable URLs from various radio-station, video & podcast websites.
4              
5             =head1 INSTALLATION
6              
7             To install this module, run the following commands:
8              
9             perl Makefile.PL
10              
11             make
12              
13             make test
14              
15             make install
16              
17             =head1 AUTHOR
18              
19             This module is Copyright (C) 2017-2026 by
20              
21             Jim Turner, C<< >>
22            
23             Email: turnerjw784@yahoo.com
24              
25             All rights reserved.
26              
27             You may distribute this module under the terms of either the GNU General
28             Public License or the Artistic License, as specified in the Perl README file.
29              
30             =head1 SYNOPSIS
31              
32             #!/usr/bin/perl
33              
34             use strict;
35              
36             use StreamFinder;
37              
38             die "..usage: $0 URL\n" unless ($ARGV[0]);
39              
40             my $station = new StreamFinder($ARGV[0]);
41              
42             die "Invalid URL or no streams found!\n" unless ($station);
43              
44             my $firstStream = $station->get();
45              
46             print "First Stream URL=$firstStream\n";
47              
48             my $url = $station->getURL();
49              
50             print "Stream URL=$url\n";
51              
52             my $stationTitle = $station->getTitle();
53            
54             print "Title=$stationTitle\n";
55            
56             my $stationDescription = $station->getTitle('desc');
57            
58             print "Description=$stationDescription\n";
59            
60             my $stationID = $station->getID();
61              
62             print "Station ID=$stationID\n";
63            
64             my $artist = $station->{'artist'};
65              
66             print "Artist=$artist\n" if ($artist);
67            
68             my $genre = $station->{'genre'};
69              
70             print "Genre=$genre\n" if ($genre);
71            
72             my $icon_url = $station->getIconURL();
73              
74             if ($icon_url) { #SAVE THE ICON TO A TEMP. FILE:
75              
76             print "Icon URL=$icon_url=\n";
77              
78             my ($image_ext, $icon_image) = $station->getIconData();
79              
80             if ($icon_image && open IMGOUT, ">/tmp/${stationID}.$image_ext") {
81              
82             binmode IMGOUT;
83              
84             print IMGOUT $icon_image;
85              
86             close IMGOUT;
87              
88             print "...Icon image downloaded to (/tmp/${stationID}.$image_ext)\n";
89              
90             }
91              
92             }
93              
94             my $stream_count = $station->count();
95              
96             print "--Stream count=$stream_count=\n";
97              
98             my @streams = $station->get();
99              
100             foreach my $s (@streams) {
101              
102             print "------ stream URL=$s=\n";
103              
104             }
105              
106             =head1 DESCRIPTION
107              
108             StreamFinder accepts a webpage URL for a valid radio station, video, or podcast
109             / episode URL on supported websites and returns the actual stream URL(s),
110             title, and cover art icon for that station / podcast / video. The purpose is
111             that one needs one of these URLs in order to have the option to stream the
112             station / podcast / video in one's own choice of media player software rather
113             than using their web browser and accepting flash, ads, javascript, cookies,
114             trackers, web-bugs, and other crapware associated with that method of play.
115             The author created and uses his own custom all-purpose media player called
116             "Fauxdacious Media Player" (his custom forked version of the open-source
117             "Audacious Audio Player). "Fauxdacious"
118             (L) incorporates this module via
119             a Perl helper-script to decode and play streams, along with their titles /
120             station names, and station / podcast / video icons, artists / channel names,
121             genres, and descriptions!
122              
123             Please NOTE: StreamFinder is a module, NOT a standalone application. It is
124             designed to be used by other Perl applications. To create your own very simple
125             application just to fetch stream data manually, simply grab the code in the
126             B section above, save it to an executable text file, ie.
127             I, and run it from the command line with a supported streaming
128             site URL as the argument. You can then edit it to tailor it to your needs.
129              
130             The currently-supported websites are:
131             podcasts.apple.com podcasts (L),
132             bitchute.com videos (L),
133             blogger.com videos (L),
134             brandnewtube.com (now onevsp.com) videos (L),
135             brighteon.com videos (L),
136             castbox.fm podcasts (L),
137             theepochtimes.com/epochtv videos (L),
138             iheart.com (aka iheartradio.com) radio stations and podcasts
139             (L),
140             www.internet-radio.com radio stations (L),
141             onlineradiobox.com radio stations (L),
142             odysee.com videos (L),
143             podbean.com podcasts (L),
144             podcastaddict.com podcasts (L),
145             podchaser.com podcasts (L),
146             prageru.com videos (L),
147             radio.net radio stations (L),
148             rcast.net radio stations (L),
149             rumble.com videos (L),
150             sermonaudio.com sermons: audio and video (L),
151             soundcloud.com (non-paywalled) songs (L)
152             (DEPRECIATED), spreaker.com podcasts (L),
153             subsplash.com podcasts (L) (EXPERIMENTAL),
154             tunein.com (non-paywalled) radio stations and podcasts
155             (L), vimeo.com videos (L),
156             youtube.com, et. al and other sites that yt-dlp support
157             (L),
158             zeno.fm radio stations and podcasts (L),
159             and L - search any (other) webpage URL (not supported
160             by any of the other submodules) for streams.
161              
162             NOTE: StreamFinder::Podcastaddict is now fully-functional again including
163             being able to return playlists from podcast pages!
164              
165             NOTE: StreamFinder::Google has been removed as Google Podcasts has shut down.
166              
167             NOTE: Users should also consider StreamFinder::SoundCloud to now be
168             depreciated, as they've added cookie and tracker requirements making it
169             impossible to search for songs on their site without enabling, but song URLs
170             (when known) seem to still work for now, but without channel/artist icons.
171             (Privacy-minded individuals should now be cautious while using this site).
172              
173             NOTE: For many sites, ie. Youtube, Vimeo, Apple, Spreaker, Castbox, PragerU,
174             etc. the "station" object actually refers to a specific video or podcast
175             episode, but functions the same way.
176              
177             Each site is supported by a separate subpackage (StreamFinder::I),
178             which is determined and selected based on the URL argument passed to it when
179             the StreamFinder object is created. The methods are overloaded by the selected
180             subpackage's methods. An example would be B.
181              
182             Please see the POD. documentation for each subpackage for important additional
183             information on options and features specific to each site / subpackage!
184              
185             One or more playable streams can be returned for each station / video /
186             podcast, along with at least a "title" (station name / video or podcast episode
187             title) and an icon image URL ("iconurl" - if found). Additional information
188             that MAY be fetched is a (larger?) banner image ("imageurl"), a (longer?)
189             "description", an "artist" / author, a "genre", and / or a "year" (podcasts,
190             videos, etc.), an AlbumArtist / channel URL, and possibly a second
191             icon image for the channel (podcasts and videos). Some sites also provide
192             radio stations' FCC call letters ("fccid"). For icon and image URLs,
193             functions exist (getIconData() and getImageData()) to fetch the actual binary
194             data and mime type for downloading to local storage for use by your
195             application or preferred media player. NOTE: StreamFinder::Anystream is not
196             able to return much beyond the stream URLs it finds, but please see it's POD
197             documentation for details on what it is able to return.
198              
199             If you have another streaming site that is not supported, first, make sure
200             you have B installed and see if B can
201             successfully fetch any streams for it. If not, then please file a feature
202             request via email or the CPAN bug system, or (for faster service), provide a
203             Perl patch module / program source that can extract some or all of the
204             necessary information for streams on that site and I'll consider it! The
205             easiest way to do this is to take one of the existing submodules, copy it to
206             "StreamFinder::I.pm", modify it (and the POD docs) to your
207             specific site's needs, test it on several of their pages (see the "SYNOPSIS"
208             code above), and send it to me (That's what I do when I want to add a
209             new site)!
210              
211             =head1 SUBROUTINES/METHODS
212              
213             =over 4
214              
215             =item B(I [, I ])
216              
217             Accepts a URL and creates and returns a new station, video, or
218             podcast object, or I if the URL is not a valid station or
219             no streams are found.
220              
221             NOTE: Depending on the type of site being queried, the "station
222             object" can be either a streaming station, a video, or a podcast,
223             but works the same way (method calls, arguments, etc.).
224              
225             NOTE: A full URL must be specified here, but if using any of the
226             subpackage modules directly instead, then either a full URL OR just
227             the station / video / podcast's site ID may be used! Reason being
228             that this function parses the full URL to determine which subpackage
229             (site) module to use.
230              
231             I can vary depending on the type of site that is
232             being queried. One option common to all sites is I<-debug>, which
233             turns on debugging output. A numeric option can follow specifying
234             the level (0, 1, or 2). 0 is none, 1 is basic, 2 is detailed.
235             Default: B<1> (if I<-debug> is specified). Warning: 2 will dump a ton
236             of output (mostly the HTML of the web page being parsed!
237              
238             One specific option (I<-omit>, added as of v1.45) permits omitting
239             specific submodules which are currently installed from being considered.
240             For example, to NOT handle Youtube videos nor use the fallback
241             "Anystream" module, specify: I<-omit> => I<"Youtube,Anystream">, which
242             will cause StreamFinder::Anystream and StreamFinder::Youtube to not be used
243             for the stream search. Default is for all installed submodules to be
244             considered. NOTE: Omitting a module from being considered when seeking
245             to match the correct module by site URL does NOT prevent that
246             module from being invoked by a selected module for an embedded link, OR
247             in the case of StreamFinder::Youtube being omitted, will still be invoked,
248             if required or needed by a non-omitted module initially selected!
249              
250             Another global option (applicable to all submodules) is the I<-secure>
251             option who's argument can be either 0 or 1 (I or I). If 1,
252             then only secure ("https://") streams will be returned. NOTE, it's
253             possible that some sites may only contain insecure ("http://") streams,
254             which won't return any streams if this option is specified. Therefore,
255             it may be necessary, if setting this option globally, to set it to
256             zero in the config. files for those specific modules, if you determine
257             that to be the case (I have not tested all sites for that). Default:
258             I<-secure> is 0 (false) - return all streams (http and https).
259              
260             Any other options (including I<-debug>) will be passed to the submodule
261             (if any) that handles the URL you pass in, but note, submodules accept
262             different options and ignore ones they do not recognize. Valid values
263             for some options can also vary across different submodules. A better
264             way to change default options for one or more submodules is to set up
265             submodule configuration files for the ones you wish to change.
266              
267             Additional options:
268              
269             I<-hls_bandwidth> => "I"
270              
271             Limit HLS (m3u8) streams that contain a list of other HLS streams of varying
272             BANDWIDTH values (in BITS per second) by selecting the highest bitrate stream
273             at or below the specified limit when I<$stream>->I is called.
274              
275             DEFAULT I<-none-> (no limiting by bitrate).
276              
277             I<-log> => "I"
278              
279             Specify path to a log file. If a valid and writable file is specified, A line
280             will be appended to this file every time one or more streams is successfully
281             fetched for a url.
282              
283             DEFAULT I<-none-> (no logging).
284              
285             I<-logfmt> specifies a format string for lines written to the log file.
286              
287             DEFAULT "I<[time] [url] - [site]: [title] ([total])>".
288              
289             The valid field I<[variables]> are: [stream]: The url of the first/best stream
290             found. [site]: The site (submodule) name matching the webpage url.
291             [url]: The url searched for streams. [time]: Perl timestamp when the line was
292             logged. [title], [artist], [album], [description], [year], [genre], [total],
293             [albumartist]: The corresponding field data returned (or "I<-na->",
294             if no value).
295              
296             =item $station->B(['playlist'])
297              
298             Returns an array of strings representing all stream URLs found.
299             If I<"playlist"> is specified, then an extended m3u playlist is returned
300             instead of stream url(s). NOTE: For podcast sites, if an author / channel
301             page url is given, rather than an individual podcast episode's url, get()
302             returns the first (latest?) podcast episode found, and get("playlist") returns
303             an extended m3u playlist containing the urls, titles, etc. for all the podcast
304             episodes found on that page url from latest to oldest.
305              
306             =item $station->B([I])
307              
308             Similar to B() except it only returns a single stream representing
309             the first valid stream found.
310              
311             Current options are: I<"random">, I<"nopls">, and I<"noplaylists">.
312             By default, the first ("best"?) stream is returned. If I<"random"> is
313             specified, then a random one is selected from the list of streams found.
314             If I<"nopls"> is specified, and the stream to be returned is a ".pls" playlist,
315             it is first fetched and the first entry (or a random entry if I<"random"> is
316             specified) is returned. This is needed by Fauxdacious Mediaplayer.
317             If I<"noplaylists"> is specified, and the stream to be returned is a
318             "playlist" (either .pls or .m3u? extension), it is first fetched and the first
319             entry (or a random entry if I<"random"> is specified) in the playlist
320             is returned.
321              
322             =item $station->B()
323              
324             Returns the number of streams found for the station.
325              
326             =item $station->B(['fccid'])
327              
328             Returns the station's site ID (default), or station's FCC
329             call-letters ("fccid") for applicable sites and stations.
330              
331             =item $station->B(['desc'])
332              
333             Returns the station's title, (or long description, if "desc" specified).
334              
335             NOTE: Some sights do not support a separate long description field,
336             so if none found, the standard title field will always be returned.
337              
338             =item $station->B(['artist'])
339              
340             Returns the URL for the station's "cover art" icon image, if any.
341              
342             Some video and podcast sites will also provide a separate artist/channel
343             icon. If B<'artist'> is specified, this icon url is returned instead,
344             if any.
345              
346             =item $station->B(['artist'])
347              
348             Returns a two-element array consisting of the extension (ie. "png",
349             "gif", "jpeg", etc.) and the actual icon image (binary data), if any.
350             This makes it easy to download the image to local storage for use by
351             your preferred media player.
352              
353             Some video and podcast sites will also provide a separate artist/channel
354             icon. If B<'artist'> is specified, this icon's data is returned instead,
355             if any.
356              
357             =item $station->B(['artist'])
358              
359             Returns the URL for the station's "cover art" banner image, if any.
360              
361             NOTE: If no "banner image" (usually a larger image) is found,
362             the "icon image" URL will be returned.
363              
364             Some video and podcast sites will also provide a separate artist/channel
365             image (usually larger). If B<'artist'> is specified, this icon url is
366             returned instead, if any.
367              
368             =item $station->B(['artist'])
369              
370             Returns a two-element array consisting of the extension (ie. "png",
371             "gif", "jpeg", etc.) and the actual station's banner image
372             (binary data). This makes it easy to download the image to
373             local storage for use by your preferred media player.
374              
375             NOTE: If no "banner image" (usually a larger image) is found,
376             the "icon image" data, if any, will be returned.
377              
378             Some video and podcast sites will also provide a separate artist/channel
379             image (usually larger). If B<'artist'> is specified, this icon's data is
380             returned instead, if any.
381              
382             =item $station->B()
383              
384             Returns the station / podcast / video's type (I).
385             (one of: "Anystream", "Apple", "BitChute", "Blogger", "Youtube", etc. -
386             depending on the sight that matched the URL).
387              
388             Some video and podcast sites will also provide a separate artist/channel
389             image (usually larger). If B<'artist'> is specified, this icon url is
390             returned instead, if any.
391              
392             =back
393              
394             =head1 CONFIGURATION FILES
395              
396             The default root location directory for StreamFinder configuration files
397             is "~/.config/StreamFinder". To use an alternate location directory,
398             specify it in the "I" environment variable, ie.:
399             B<$ENV{STREAMFINDER} = "/etc/StreamFinder">.
400              
401             =over 4
402              
403             =item ~/.config/StreamFinder/config
404              
405             Optional text file for specifying various configuration options.
406             Each option is specified on a separate line in the formats below:
407             NOTE: Do not follow the lines with a semicolon, comma, or any other
408             separator. Non-numeric I should be surrounded with quotes, either
409             single or double. Blank lines and lines beginning with a "#" sign as
410             their first non-blank character are ignored as comments.
411              
412             'option' => 'value' [, ...]
413              
414             'option' => ['value1', 'value2', ...] [, ...]
415              
416             'option' => {'key1' => 'value1', 'key2' => 'value2', ...} [, ...]
417              
418             and the options are loaded into a hash used by all sites
419             (submodules) that support them. Valid options include
420             I<-debug> => [0|1|2] and most of the L options.
421              
422             =item ~/.config/StreamFinder/I/config
423              
424             Optional text file for specifying various configuration options
425             for a specific site (submodule, ie. "Youtube" for
426             StreamFinder::Youtube). Each option is specified on a separate
427             line in the formats below:
428              
429             'option' => 'value' [, ...]
430              
431             'option' => ['value1', 'value2', ...] [, ...]
432              
433             'option' => {'key1' => 'value1', 'key2' => 'value2', ...} [, ...]
434              
435             and the options are loaded into a hash used only by the specific
436             (submodule) specified. Valid options include
437             I<-debug> => [0|1|2] and most of the L options.
438              
439             NOTE: Options specified here override any specified in I<~/.config/StreamFinder/config>.
440              
441             =back
442              
443             NOTE: Options specified in the options parameter list of the I
444             function will override those corresponding options specified in these files.
445              
446             =head1 DEPENDENCIES
447              
448             L, L, L
449              
450             =head1 RECCOMENDS
451              
452             yt-dlp (for Youtube, Bitchute, Blogger, Brighteon, Odysee, Vimeo)
453             NOTE: Required for Youtube, Bitchute, and SoundCloud to work.
454              
455             wget
456              
457             =head1 BUGS
458              
459             Please report any bugs or feature requests to C, or through
460             the web interface at L.
461             I will be notified, and then you'llautomatically be notified of progress on
462             your bug as I make changes.
463              
464             =head1 SUPPORT
465              
466             You can find documentation for this module with the perldoc command.
467              
468             perldoc StreamFinder
469              
470             You can also look for information at:
471              
472             =head1 SEE ALSO
473              
474             Fauxdacious media player - (L)
475              
476             =over 4
477              
478             =item * RT: CPAN's request tracker (report bugs here)
479              
480             L
481              
482             =item * Search CPAN
483              
484             L
485              
486             =back
487              
488             =head1 LICENSE AND COPYRIGHT
489              
490             Copyright 2017-2026 Jim Turner.
491              
492             This program is free software; you can redistribute it and/or modify it
493             under the terms of the the Artistic License (2.0). You may obtain a
494             copy of the full license at:
495              
496             L
497              
498             Any use, modification, and distribution of the Standard or Modified
499             Versions is governed by this Artistic License. By using, modifying or
500             distributing the Package, you accept this license. Do not use, modify,
501             or distribute the Package, if you do not accept this license.
502              
503             If your Modified Version has been derived from a Modified Version made
504             by someone other than you, you are nevertheless required to ensure that
505             your Modified Version complies with the requirements of this license.
506              
507             This license does not grant you the right to use any trademark, service
508             mark, tradename, or logo of the Copyright Holder.
509              
510             This license includes the non-exclusive, worldwide, free-of-charge
511             patent license to make, have made, use, offer to sell, sell, import and
512             otherwise transfer the Package with respect to any patent claims
513             licensable by the Copyright Holder that are necessarily infringed by the
514             Package. If you institute patent litigation (including a cross-claim or
515             counterclaim) against any party alleging that the Package constitutes
516             direct or contributory patent infringement, then this Artistic License
517             to you shall terminate on the date that such litigation is filed.
518              
519             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
520             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
521             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
522             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
523             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
524             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
525             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
526             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
527              
528             =cut
529              
530             package StreamFinder;
531              
532             require 5.001;
533              
534 1     1   143019 use strict;
  1         2  
  1         44  
535 1     1   5 use warnings;
  1         2  
  1         68  
536 1     1   8 use vars qw(@ISA @EXPORT $VERSION);
  1         2  
  1         2403  
537              
538             our $VERSION = '2.62';
539             our $DEBUG = 0;
540              
541             require Exporter;
542              
543             @ISA = qw(Exporter);
544             @EXPORT = qw();
545             my @supported_mods = (qw(Anystream Apple Bitchute Blogger BrandNewTube Brighteon Castbox EpochTV
546             Google IHeartRadio InternetRadio Odysee OnlineRadiobox Podbean PodcastAddict Podchaser
547             PragerU RadioNet Rcast Rumble SermonAudio SoundCloud Spreaker Tunein Vimeo Youtube Zeno
548             Subsplash));
549              
550             my %useit;
551              
552             foreach my $module (@supported_mods)
553             {
554             $useit{$module} = 1;
555             }
556              
557             sub new
558             {
559 0     0 1   my $class = shift;
560 0           my $url = shift;
561              
562 0           my $self = {};
563 0 0         return undef unless ($url);
564              
565 0           my $arg;
566 0           my @args = ();
567 0           while (@_) {
568 0           $arg = shift(@_);
569 0 0         if ($arg =~ /^\-?omit$/o) { #ALLOW USER TO OMIT SPECIFIC INSTALLED SUBMODULE(S):
570 0           my @omitModules = split(/\,\s*/, shift(@_));
571 0           foreach my $omit (@omitModules)
572             {
573 0           $useit{$omit} = 0;
574             }
575             } else {
576 0           push @args, $arg;
577             }
578             }
579              
580 0           my $haveit = 0;
581 0 0         push @args, ('-debug', $DEBUG) if ($DEBUG);
582 0 0 0       if ($url =~ m#\b(?:podcasts?|music)\.apple\.com\/# && $useit{'Apple'}) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
583 0           eval { require 'StreamFinder/Apple.pm'; $haveit = 1; };
  0            
  0            
584 0 0         return new StreamFinder::Apple($url, @args) if ($haveit);
585             } elsif ($url =~ m#\brumble\.com\/# && $useit{'Rumble'}) {
586 0           eval { require 'StreamFinder/Rumble.pm'; $haveit = 1; };
  0            
  0            
587 0 0         return new StreamFinder::Rumble($url, @args) if ($haveit);
588             } elsif ($url =~ m#\bpodcastaddict\.# && $useit{'PodcastAddict'}) {
589 0           eval { require 'StreamFinder/PodcastAddict.pm'; $haveit = 1; };
  0            
  0            
590 0 0         return new StreamFinder::PodcastAddict($url, @args) if ($haveit);
591             } elsif ($url =~ m#\b(?:onevsp|brandnewtube)\.# && $useit{'BrandNewTube'}) {
592 0           eval { require 'StreamFinder/BrandNewTube.pm'; $haveit = 1; };
  0            
  0            
593 0 0         return new StreamFinder::BrandNewTube($url, @args) if ($haveit);
594             } elsif ($url =~ m#\bbitchute\.# && $useit{'Bitchute'}) {
595 0           eval { require 'StreamFinder/Bitchute.pm'; $haveit = 1; };
  0            
  0            
596 0 0         return new StreamFinder::Bitchute($url, @args) if ($haveit);
597             } elsif ($url =~ m#\biheart(?:radio)?\.#i && $useit{'IHeartRadio'}) {
598 0           eval { require 'StreamFinder/IHeartRadio.pm'; $haveit = 1; };
  0            
  0            
599 0 0         return new StreamFinder::IHeartRadio($url, @args) if ($haveit);
600             } elsif ($url =~ m#\btunein\.# && $useit{'Tunein'}) { #NOTE:ALSO USES yt-dlp!
601 0           eval { require 'StreamFinder/Tunein.pm'; $haveit = 1; };
  0            
  0            
602 0 0         return new StreamFinder::Tunein($url, @args) if ($haveit);
603             } elsif ($url =~ m#\bbrighteon\.com\/# && $useit{'Brighteon'}) { #NOTE:ALSO USES yt-dlp!
604 0           eval { require 'StreamFinder/Brighteon.pm'; $haveit = 1; };
  0            
  0            
605 0 0         return new StreamFinder::Brighteon($url, @args) if ($haveit);
606             } elsif ($url =~ m#\bspreaker\.# && $useit{'Spreaker'}) {
607 0           eval { require 'StreamFinder/Spreaker.pm'; $haveit = 1; };
  0            
  0            
608 0 0         return new StreamFinder::Spreaker($url, @args) if ($haveit);
609             } elsif ($url =~ m#\bcastbox\.\w+\/# && $useit{'Castbox'}) {
610 0           eval { require 'StreamFinder/Castbox.pm'; $haveit = 1; };
  0            
  0            
611 0 0         return new StreamFinder::Castbox($url, @args) if ($haveit);
612             } elsif ($url =~ m#\bradio\.net\/# && $useit{'RadioNet'}) {
613 0           eval { require 'StreamFinder/RadioNet.pm'; $haveit = 1; };
  0            
  0            
614 0 0         return new StreamFinder::RadioNet($url, @args) if ($haveit);
615             } elsif ($url =~ m#\bvimeo\.# && $useit{'Vimeo'}) { #NOTE:ALSO USES yt-dlp!
616 0           eval { require 'StreamFinder/Vimeo.pm'; $haveit = 1; };
  0            
  0            
617 0 0         return new StreamFinder::Vimeo($url, @args) if ($haveit);
618             } elsif ($url =~ m#\bprageru\.# && $useit{'PragerU'}) {
619 0           eval { require 'StreamFinder/PragerU.pm'; $haveit = 1; };
  0            
  0            
620 0 0         return new StreamFinder::PragerU($url, @args) if ($haveit);
621             } elsif ($url =~ m#\bblogger\.# && $useit{'Blogger'}) {
622 0           eval { require 'StreamFinder/Blogger.pm'; $haveit = 1; };
  0            
  0            
623 0 0         return new StreamFinder::Blogger($url, @args) if ($haveit);
624             } elsif ($url =~ m#\bsermonaudio\.com\/# && $useit{'SermonAudio'}) {
625 0           eval { require 'StreamFinder/SermonAudio.pm'; $haveit = 1; };
  0            
  0            
626 0 0         return new StreamFinder::SermonAudio($url, @args) if ($haveit);
627             } elsif ($url =~ m#\bodysee\.com\/# && $useit{'Odysee'}) {
628 0           eval { require 'StreamFinder/Odysee.pm'; $haveit = 1; };
  0            
  0            
629 0 0         return new StreamFinder::Odysee($url, @args) if ($haveit);
630             } elsif ($url =~ m#\bpodbean\.com\b# && $useit{'Podbean'}) {
631 0           eval { require 'StreamFinder/Podbean.pm'; $haveit = 1; };
  0            
  0            
632 0 0         return new StreamFinder::Podbean($url, @args) if ($haveit);
633             } elsif ($url =~ m#\bonlineradiobox\.# && $useit{'OnlineRadiobox'}) {
634 0           eval { require 'StreamFinder/OnlineRadiobox.pm'; $haveit = 1; };
  0            
  0            
635 0 0         return new StreamFinder::OnlineRadiobox($url, @args) if ($haveit);
636             } elsif ($url =~ m#\binternet\-radio\.# && $useit{'InternetRadio'}) {
637 0           eval { require 'StreamFinder/InternetRadio.pm'; $haveit = 1; };
  0            
  0            
638 0 0         return new StreamFinder::InternetRadio($url, @args) if ($haveit);
639             } elsif ($url =~ m#\bsoundcloud\.# && $useit{'SoundCloud'}) {
640 0           eval { require 'StreamFinder/SoundCloud.pm'; $haveit = 1; };
  0            
  0            
641 0 0         return new StreamFinder::SoundCloud($url, @args) if ($haveit);
642             } elsif ($url =~ m#\brcast\.# && $useit{'Rcast'}) {
643 0           eval { require 'StreamFinder/Rcast.pm'; $haveit = 1; };
  0            
  0            
644 0 0         return new StreamFinder::Rcast($url, @args) if ($haveit);
645             } elsif ($url =~ m#\bpodchaser\.# && $useit{'Podchaser'}) {
646 0           eval { require 'StreamFinder/Podchaser.pm'; $haveit = 1; };
  0            
  0            
647 0 0         return new StreamFinder::Podchaser($url, @args) if ($haveit);
648             } elsif ($url =~ m#\bzeno\.# && $useit{'Zeno'}) {
649 0           eval { require 'StreamFinder/Zeno.pm'; $haveit = 1; };
  0            
  0            
650 0 0         return new StreamFinder::Zeno($url, @args) if ($haveit);
651             } elsif ($url =~ m#\bsubsplash\.# && $useit{'Subsplash'}) {
652 0           eval { require 'StreamFinder/Subsplash.pm'; $haveit = 1; };
  0            
  0            
653 0 0         return new StreamFinder::Subsplash($url, @args) if ($haveit);
654             } elsif ($url =~ m#\btheepochtimes\.# && $useit{'EpochTV'}) {
655 0           eval { require 'StreamFinder/EpochTV.pm'; $haveit = 1; };
  0            
  0            
656 0 0         return new StreamFinder::EpochTV($url, @args) if ($haveit);
657             } elsif ($url !~ /\.m3u8$/i && $useit{'Youtube'}) {
658             #DEFAULT TO yt-dlp (EXCEPT HLS URLS) SINCE SO MANY URLS ARE HANDLED THERE NOW.
659             #(WE NOW PASS HLS URLS ON TO Anystream WHICH CHECKS THEM AGAINST ANY BANDWIDTH
660             #LIMITS AND, IF A MASTER PLAYLIST, LIMITS TO STREAMS WITHIN THE LIMITS):
661 0           eval { require 'StreamFinder/Youtube.pm'; $haveit = 1; };
  0            
  0            
662 0 0         if ($haveit) {
663 0           my $yt = new StreamFinder::Youtube($url, @args);
664 0 0 0       return $yt if (defined($yt) && $yt && $yt->count() > 0);
      0        
665             }
666             }
667 0 0         if ($useit{'Anystream'}) { #SITE NOT SUPPORTED, TRY TO FIND ANY STREAM URLS WE CAN:
668 0           $haveit = 0;
669 0           eval { require 'StreamFinder/Anystream.pm'; $haveit = 1; };
  0            
  0            
670 0 0         return new StreamFinder::Anystream($url, @args) if ($haveit);
671             }
672 0           return undef;
673             }
674              
675             1