NAME
    LyricFinder - Fetch song lyrics from several internet lyric sites.

AUTHOR
    This module is Copyright (c) 2020-2025 by

    Jim Turner, "<turnerjw784 at yahoo.com>"

    All rights reserved.

    This library is free software; you can redistribute it and/or modify it
    under the terms of either the GNU General Public License or the Artistic
    License, as specified in the Perl README file.

    NOTE: This is a "derived work" of Lyrics::Fetcher family of modules, by
    (c) David Precious (davidp at preshweb.co.uk) (CPAN Id: BIGPRESH), as
    fair use legal under the terms of, subject to, and licensed in terms
    compatable and compliant with those modules. Many thanks to David for
    laying the groundwork for this module!

SYNOPSIS
        #!/usr/bin/perl

        use LyricFinder;

        # create a new finder object:
        my $finder = new LyricFinder();

        # fetch lyrics for a song from a specific site (https://www.azlyrics.com):
        print $finder->fetch('Pink Floyd','Echoes','AZLyrics');

        # if you omit the site, automatically tries all available
        # sites in random order:
        print $finder->fetch('Oasis', 'Cast No Shadow');

        # or you can pass an arrayref of sites you want used in order:
        print $finder->fetch('Oasis', 'Whatever', [qw(Genius AZLyrics)]);

        # To find out which site modules are available:
        my @fetchers = $finder->sources();

        # To fetch the source (site) name and base url:
        print "(Lyrics courtesy: ".$finder->source().")\n";
        print "site url:  ".$finder->site().")\n";

        # To show what sites we had to search for these lyrics:
        print "..Tried sites:  ".$finder->tried().".\n";

        # To do caching:
        $finder->cache('/tmp/lyrics');
        #-or-
        my $localfinder = new LyricFinder(-cache => '/tmp/lyrics');

        #-or- to only fetch lyrics from our cache:
        my $local_lyrics = $localfinder->fetch('Oasis', 'Cast No Shadow', 'Cache');
        if ($local_lyrics) {
            print "..Lyrics from disk:\n\n$local_lyrics\n";
        } else {
            print "..No local lyrics found for 'Cast No Shadow', by Oasis.\n";
        }

DESCRIPTION
    LyricFinder accepts an artist name and song title, searches supported
    lyrics sites for song lyrics, and, if found, returns them as a string.

    The supported and currently-installed modules are:
    LyricFinder::ApiLyricsOvh (for searching api.lyrics.ovh),
    LyricFinder::AZLyrics (www.azlyrics.com), LyricFinder::ChartLyrics
    (api.chartlyrics.com), LyricFinder::Genius (genius.com),
    LyricFinder::Letras (www.letras.mus.br), LyricFinder::Lrclib
    (lrclib.net), and LyricFinder::Musixmatch (www.musixmatch.com). There is
    a special module for storing and / or fetching lyrics (.lrc) files
    already stored locally, called LyricFinder::Cache.

    This module is derived from the (older) Lyrics::Fetcher collection of
    modules by (c) 2007-2020 David Precious, but currently supports more
    lyric sites (7) and bundles all the supported site modules together here
    (simply install this one module). We have reworked the "Cache" module to
    cache lyrics files by artist and song title on disk in the user's
    desired location. LyricFinder is also truly object-oriented making
    interaction with the user-facing methods and data easier and more
    streamlined.

    NOTE: This module is used completely independent of any of those
    modules, but some of the code is derived from them, as allowed by and
    the license and credits are included here, as required by their
    open-source license. It is capable of being used as a drop-in
    replacement, but some function names and other code changes will be
    needed.

    We've also added methods to easily change the "user-agent" passed to the
    lyrics sites, as needed/desired by the user programatically.

    We've also changed the default to search the supported sites randomly,
    instead of in the same fixed alphabetical order by module name
    ("load-balancing" the searches to all the sites). This is helpful when
    using LyricFinder as a plugin for streaming media players, such as the
    author's very own "Fauxdacious Media Player" that frequently stream
    internet radio stations, which can impose a "hit" on the lyrics sites
    each time the song title changes. This reduces the odds of a user's
    IP-address possibly being banned by a lyrics site for "too-frequent
    scraping / usage"! NOTE: If you want to prevent the usage of one or more
    of the specific sites, simply specify the *-omit* argument with a
    reference to a list of module names to skip, ie. *-omit =*>
    *'AZLyrics,Genius'* in the *new LyricFinder()* function. If you want to
    use one or more specific sites, or enforce a specific search order, you
    can call the fetch() method with a third argument consisting of the site
    module name, ie. "Musixmatch", or reference to an array of site module
    names, ie. [Genius, AZLyrics]. If you specify "Cache" as the single
    module name (and provide a "-cache" directory containing lyrics files on
    your hard drive), LyricFinder will only search that directory for
    lyrics, and not the internet. Otherwise, specifying a "-cache" directory
    will cause LyricFinder to first look in your cache directory for
    matching lyrics first, and only search any of the lyrics sites if not
    found, then will cache the lyrics found on the internet to a new lyric
    file in your lyrics directory eleminating re-searching the web when you
    play the same song again later and reducing internet bandwidth usage!

    In case of problems with fetching lyrics, the error string will be
    returned by $finder->message(). If all goes well, it will have 'Ok' in
    it.

    The site selection is made by the "method" parameter passed to the
    fetch() of this module. You can also omit this parameter, in which case
    all available fetchers will be tried in random order, or you can supply
    an arrayref of sites you'd like to try in the order you specify.

    The value of the "method" parameter (if specified) must be either the
    "*" part of one of the installed LyricFinder::* fetcher package name,
    "all", or "random".

    If you have another lyrics site that is not supported, please file a
    feature request via email or the CPAN bug system, or (for faster
    service), provide a Perl patch module / program source that can extract
    lyrics from that site and I'll consider it! The easiest way to do this
    is to take one of the existing submodules, copy it to
    "LyricFinder::*YOURSITE*.pm and modify it (and the POD docs) to your
    specific site's needs, test it with several Artist / Title combinations
    (see the "SYNOPSIS" code above), and send it to me (That's what I do for
    new sites)!

INSTALLATION
            To install this module, run the following commands:

            perl Makefile.PL

            make

            make test

            make install

SUBROUTINES/METHODS
    new *LyricFinder*([ *options* ])
        Creates a new finder object for fetching lyrics. The same finder
        object can be used for multiple fetches from multiple sites, so this
        normally only needs to be called once.

        *options* is a hash of option/value pairs (ie. "-option" =>
        "value"). If an "-option" is specified with no "value" given, the
        default value will be *1* ("*true*"). The currently-supported
        options are:

        -agent => *"user-agent string"*
            Specifies an alternate "user-agent" string sent to the lyric
            sites when attempting to fetch lyrics. Get / set the desired
            user-agent (ie. browser name) to pass to the lyrics sites. Some
            sites are pickey about receiving a user-agent string that
            corresponds to a valid / supported web-browser to prevent their
            sites from being "scraped" by programs, such as this.

            Default: *"Mozilla/5.0 (X11; Linux x86_64; rv:112.0)
            Gecko/20100101 Firefox/112.0"*.

            NOTE: This value will be overridden if $finder->agent("agent")
            is called! NOTE: See below how to specify a different agent for
            a specific site module.

        -cache => *"directory"*
            Specifies a directory (ie. "/home/user/Music/Lyricsfiles") to be
            used for disk caching. If specified, this directory will be
            searched for a matching lyrics (.lrc) file (in this example,
            "/home/user/Music/LyricsFiles/*artist*/*title*.lrc"). If no
            matching lyrics file is found (and the search module list is not
            specifically set to "Cache" (and lyrics are found on the
            internet), then the lyrics will be saved to the directory under
            "*artist*/*title*.lrc".

            Default: *none* (no caching)

            An optional dirctional indicator ("<" or ">") can be prepended
            to the directory to limit caching activity. "<" allows fetching
            lyrics from the cache directory, but will not cache (write) new
            lyrics found on the web to the directory. ">" (the opposite)
            will cache new lyrics but will never attempt to fetch (read)
            lyrics from the cache directory. These options may be useful if
            one either simply wants to build a lyrics database but always
            fetch the latest, or perhaps limit lyrics to a fixed cache but
            not add to it, or perhaps is using a readonly directory. The
            default is no indicator which allows both reading and writing.

            Directory must be a valid directory, but may be specified as
            either a path (ie. "/home/user/lyrics") or a URI (ie.
            "file:///home/user/lyrics") or with a limiting directional
            indicator, ie. "</home/user/lyrics". It may or may not have a
            trailing "/" (ie. "/home/user/lyrics/").

            NOTE: This value will be overridden if
            $finder->cache("directory") is called! NOTE: See below how to
            specify a different agent for a specific site module.

        -debug => *number*
            Specifies whether debug information will be displayed (0: no,
            >0: yes).

            Default *0* (no). *1* will display debug info. There is
            currently only one level of debug verbosity.

        -noextra => *0 | 1* (*false* or *true*)
            Some sites (currently, only Musixmatch) may append some
            additional information below the lyrics text. If specified or
            given a *true* value, this will be suppressed and only the
            actual song lyrics returned. Default *0* (false) - show any
            additional site-specific information.

        -omit => *"site-module[, site-module2...]"*
            Permits omitting specific sites which are currently installed
            from being searched (namely when using *random* or *all*). For
            example, to exclude the Musixmatch site, specify: *-omit* =>
            *"Musixmatch"*, which will cause LyricFinder::Musixmatch to not
            be considered for lyrics search. Default is for all installed
            sites (submodules) to be considered. NOTE: The site list can be
            specified as a comma-separated string OR as an array reference,
            ie. *-omit =* [ qw(Musixmatch Genius) ]>.

        -site-module-name => { *"-option"* => *value* [, *"-option"* =>
        *value* ... ] }
            Specifies options for a specific site fetcher module. These
            values will override any of the general option values specified
            for that specific module or calls to the general agent() method,
            if it is used to fetch lyrics. Examples would be if one needed
            to specify a different user-agent for one of the sites, or
            wished to cache lyrics fetched by sites to specific directories
            for some reason. By default, top-level options are passed to the
            various sites, so this should only be needed in special cases.

            Example: "-Musixmatch => { -noextra => 1 }"

            Default: none (no site-specific options)

            NOTE: The "-cache" (cache-directory) option is needed by the
            main LyricFinder module and the site submodules in order to use
            the caching feature, so passing "-Cache => {-cache =>
            *directory*}" will NOT work (the way one might assume)!

        -synced => *"YES"* | *"NO"* | *"OK"* | *"ONLY"*
            Some sites can return either timestamp-synced ("synced") lyrics
            or a plain-text version. "YES" specifies: First try synced, if
            none, then try plain-text. "NO" specifies: only try to return
            the plain-text vsn. "ONLY" specifies: only try to return the
            synced version, and "OK" specifies: First try the plain-text
            vsn. if none, then try the synced vsn.. Currently, only the
            LyricFinder::Lrclab module supports returning timestamp-synced
            lyrics. Other modules currently ignore this option.

            Default *""* (false), which is treated same as "NO" (plain-text
            only, if available, otherwise no lyrics returned).

            Currently, the only known site offering "synced" lyrics is
            LyricFinder::Lrclib, but will serve up "plain-text" lyrics, by
            default, as the default is "NO" if this option false or not
            specified (Except is *-synced* => *"ONLY"* is specified, in
            which either timestamped lyrics or no lyrics will be returned).

    [ *$current-agent-string* = ] $finder->agent( [ *user-agent-string* ] )
        Get / Set the desired user-agent (ie. browser name) to pass to the
        lyrics sites. Some sites are pickey about receiving a user-agent
        string that corresponds to a valid / supported web-browser to
        prevent their sites from being "scraped" by programs, such as this.

        Default: *"Mozilla/5.0 (X11; Linux x86_64; rv:112.0) Gecko/20100101
        Firefox/112.0"*

        If no argument is passed, it returns the current GENERAL user-agent
        string in effect (but a different agent option is specified for a
        specific module may have been specified and used by THAT module -
        see new() options above).

        NOTE: This will override any -agent option value specified in new()!
        NOTE: See above how to specify a different user-agent for a specific
        site module.

    [ *$current-directory* = ] $finder->cache( [ *$directory* ] )
        Specifies a directory (ie. "/home/user/Music/Lyricsfiles") to be
        used for disk caching. If specified, this directory will be searched
        for a matching lyrics (.lrc) file (in this example,
        "/home/user/Music/LyricsFiles/*artist*/*title*.lrc"). If no matching
        lyrics file is found (and the search module list is not specifically
        set to "Cache" (and lyrics are found on the internet), then the
        lyrics will be saved to the directory under "*artist*/*title*.lrc".

        An optional dirctional indicator ("<" or ">") can be prepended to
        the directory to limit caching activity. "<" allows fetching lyrics
        from the cache directory, but will not cache (write) new lyrics
        found on the web to the directory. ">" (the opposite) will cache new
        lyrics but will never attempt to fetch (read) lyrics from the cache
        directory. These options may be useful if one either simply wants to
        build a lyrics database but always fetch the latest, or perhaps
        limit lyrics to a fixed cache but not add to it, or perhaps is using
        a readonly directory. The default is no indicator which allows both
        reading and writing.

        Directory must be a valid directory, but may be specified as either
        a path (ie. "/home/user/lyrics") or a URI (ie.
        "file:///home/user/lyrics") or with a limiting directional
        indicator, ie. "</home/user/lyrics". It may or may not have a
        trailing "/" (ie. "/home/user/lyrics/").

        If no argument is passed, it returns the current GENERAL cache
        directory string in effect (but a different directory option is
        specified for a specific module may have been specified and used by
        THAT module - see new() options above).

        NOTE: This will override any -cache option value specified in new()!

        NOTE: See above how to specify a different directory for a specific
        site module.

    [ *$scalar* | *@array* ] = $finder->credits()
        Returns either a comma-separated list or an array of names credited
        by the site with posting the lyrics on the site (if any) or an empty
        string, if none found. NOTE: The only site that supports this
        currently is AZLyrics.

    *$string* = $finder->fetch(*$artist*, *$title* [, *$source[.
    source2...]* | *\@sources* [, *$limit*]])
        Attempt to fetch the lyrics for the given artist and title. One or
        more source site module names can be specified as either a comma-
        separated string or in an array-reference, [module1, module2...], or
        "Cache" (the Cache module), "All" or "Auto" specified alone. The
        value "random" may appear either alone or in a list (string or
        array-reference), which will cause any module(s) listed before it to
        be searched first, any after it to be searched last, and all the
        other ones available (but not in any *-omit* list (see further
        above) to be searched in between (assuming lyrics are not found in
        any modules listed preceeding it). "ALL" or "Auto" (synanyms) are
        equivalent to the full list of modules in their fixed, alphabetical
        order.

        Default: *"random"* (search all available sites in random order
        until lyrics are found or all available sites have been searched).

        fetch()() is the primary method call, and the only one required
        (besides new()) to be called to obtain lyrics. $limit (if specified)
        is an integer number to limit the max. number of fetchers to try
        (normally used with $source = "random") to limit the time needed to
        search for lyrics (before giving up). If not specified, zero, or
        higher than the number of installed fetcher modules, then all
        available (installed) fetcher submodules (sites) will be tried
        (until one succefully finds lyrics).

        "Cache" is a special module that limits searching to a specified
        lyrics directory on one's local hard drive. NOTE: It should NOT be
        included in a list, but used by itself. If a lyrics directory is
        specified, Cache will automatically be searched first!

        If a list of modules is provided, they will be searched in the order
        they appear in the list.

        The currently-installed and supported modules are: ApiLyricsOvh,
        AZLyrics, ChartLyrics, Genius, Letras, Lrclib, and Musixmatch (NOTE
        the "x" in the spelling of "Musixmatch")!

        fetch()() returns lyrics as a string (includes line-breaks
        appropriate for the user's operating system), or an empty string, if
        no lyrics found.

    [ *$lyrics-option-string* = ] $finder->fetch_synced_lyrics( [ *string* )
        Get / Set the desired synced-lyrics fetching option. The valid
        string values are / returned are: *"YES"* | *"NO"* | *"OK"* |
        *"ONLY"*.

        Some sites can return either timestamp-synced ("synced") lyrics or a
        plain-text version. "YES" specifies: First try synced, if none, then
        try plain-text. "NO" specifies: only try to return the plain-text
        vsn. "ONLY" specifies: only try to return the synced version, and
        "OK" specifies: First try the plain-text vsn. if none, then try the
        synced vsn. Currently, only the LyricFinder::Lrclab module supports
        returning timestamp-synced lyrics. Other modules currently ignore
        this option.

    *$scalar* = $finder->image_url()
        Returns a URL for a cover-art image, if one found on the lyrics
        page. Currently, only the LyricFinder::ChartLyrics,
        LyricFinder::Genius and LyricFinder::Musixmatch sites contain
        cover-art images. For the other sites, or if no image is found, an
        empty string will be returned if this method is called.

    *$scalar* = $finder->message()
        Returns the last error string generated, or "Ok" if all's well.

    [ *$scalar* | *@array* ] = $finder->order()
        Returns either a comma-separated list or an array of the site
        modules tried by the last fetch. This is useful to see what sites
        are being tried and in what order if *random* order is being used.
        Similar to tried(), except all sites being considered are shown.

    *$scalar* = $finder->site()
        Returns the actual base URL of the site that successfully fetched
        the lyrics in the last successful fetch (or an empty string if the
        fetch failed).

    *$scalar* = $finder->source()
        Returns the name of the module that successfully fetched the lyrics
        in the last successful fetch (or "none" if the fetch failed).

    [ *$arrayref* | *@array* ] = $finder->sources()
        Returns a list of available site modules. Similar to
        Lyrics::Fetcher's *available_fetchers*() function.

    [ *$scalar* | *@array* ] = $finder->tried()
        Returns either a comma-separated list or an array of the site
        modules actually tried when fetching lyrics. This is useful to see
        what sites were actually hit and in what order if *random* order is
        being used. Similar to order(), except only sites actually hit are
        shown (the last one is the one that successfully fetched the
        lyrics).

    *$scalar* = $finder->url()
        Returns the actual URL used to fetch the lyrics from the site
        (includes the actual formatted search arguments passed to the site).
        This can be helpful in debugging, etc.

        NOTE: If caching is being used and lyrics are found and fetched from
        the cache directory, site() will return the full filename of the
        cache file fetched.

    $LyricFinder::VERSION
        The current version# of LyricFinder

DEPENDENCIES
    HTML::Strip, HTTP::Request, LWP::UserAgent, URI::Escape

RECOMMENDS
    XML::Simple (required to use LyricFinder::ChartLyrics)

BUGS
    Please report any bugs or feature requests to "bug-lyricFinder at
    rt.cpan.org", or through the web interface at
    <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=LyricFinder>. I will be
    notified, and then you'll automatically be notified of progress on your
    bug as I make changes.

SUPPORT
    You can find documentation for this module with the perldoc command.

        perldoc LyricFinder

SEE ALSO
    Fauxdacious media player -
    (<https://wildstar84.wordpress.com/fauxdacious>)

    *   RT: CPAN's request tracker (report bugs here)

        <http://rt.cpan.org/NoAuth/Bugs.html?Dist=LyricFinder>

    *   CPAN Ratings

        <http://cpanratings.perl.org/d/LyricFinder>

    *   Search CPAN

        <http://search.cpan.org/dist/LyricFinder/>

LICENSE AND COPYRIGHT
    Copyright (c) 2020-2025 Jim Turner.

    This program is free software; you can redistribute it and/or modify it
    under the terms of the the Artistic License (2.0). You may obtain a copy
    of the full license at:

    <http://www.perlfoundation.org/artistic_license_2_0>

    Any use, modification, and distribution of the Standard or Modified
    Versions is governed by this Artistic License. By using, modifying or
    distributing the Package, you accept this license. Do not use, modify,
    or distribute the Package, if you do not accept this license.

    If your Modified Version has been derived from a Modified Version made
    by someone other than you, you are nevertheless required to ensure that
    your Modified Version complies with the requirements of this license.

    This license does not grant you the right to use any trademark, service
    mark, tradename, or logo of the Copyright Holder.

    This license includes the non-exclusive, worldwide, free-of-charge
    patent license to make, have made, use, offer to sell, sell, import and
    otherwise transfer the Package with respect to any patent claims
    licensable by the Copyright Holder that are necessarily infringed by the
    Package. If you institute patent litigation (including a cross-claim or
    counterclaim) against any party alleging that the Package constitutes
    direct or contributory patent infringement, then this Artistic License
    to you shall terminate on the date that such litigation is filed.

    Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
    AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
    THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
    PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
    YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
    CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
    CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
    EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    Original Lyrics::Fetcher::* work:

    Copyright (C) 2007-2020 David Precious.

    This library is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself, either Perl version 5.8.7 or, at
    your option, any later version of Perl 5 you may have available.

    Legal disclaimer: I have no connection with the owners of any of these
    sites. Lyrics fetched by this script may be copyrighted by the authors,
    it's up to you to determine whether this is the case, and if so, whether
    you are entitled to request/use those lyrics. You will almost certainly
    not be allowed to use the lyrics obtained for any commercial purposes.

    All comments / suggestions / bug reports gratefully received (ideally
    use the RT installation at <https://rt.cpan.org/> or through the web
    interface at
    <https://rt.cpan.org/NoAuth/ReportBug.html?Queue=LyricFinder>, or mail
    me direct if you prefer).

    Developed on Github at <https://github.com/bigpresh/LyricFinder>

    Previously: Copyright 2003 Sir Reflog <reflog@mail15.com>. Copyright
    2003 Zachary P. Landau <kapheine@hypa.net>

