Jump to content

Create a metadata fetcher for Remote Lyric files, having issues...


Recommended Posts

mickle026
Posted

I am trying to create a remote metadata provider for the songtrack level and cannot get it to work.

Is there actually a trigger for metadata at the song track level?

To be clear, I can do this at album level and I can also create a local reader for songs, that actually goes online and achives this, but I am trying to impliment a remote metadataProvider for just lyrics.

 

namespace LyricsProvider
{
    public class LyricsMetadataProvider : IRemoteMetadataProvider<Audio, SongInfo>
    {
        private readonly IServerConfigurationManager configurationManager;
        private readonly IProviderManager providerManager;
        private readonly ILibraryManager libraryManager;

        private LyricsFetcher lyricsFetcher;
        private IFileSystem fileSystem;

        public LyricsMetadataProvider(LyricsFetcher lyricsfetcher, IFileSystem filesystem, ILibraryManager libMan, IProviderManager provManager, IServerConfigurationManager serverConf)
        {
            providerManager = provManager;
            configurationManager = serverConf;
            libraryManager = libMan;
            lyricsFetcher = lyricsfetcher;
            fileSystem = filesystem;
        }

        public async Task<MetadataResult<Audio>> GetMetadata(Audio searchResult, string language, CancellationToken cancellationToken)
        {
            string trackartist = searchResult.Artists[0];
            string LogPath = configurationManager.ApplicationPaths.LogDirectoryPath;
            string currentlog = Path.Combine(LogPath, $"LyricsFetcher" + DateTime.Now.ToString("dd.MMM.yyy-HH'.'mm'.'ss") + ".txt");

            string songid = string.Empty;
            if (searchResult.HasProviderId(MetadataProviders.MusicBrainzTrack))
            {
                try { songid = searchResult.GetProviderId(MetadataProviders.MusicBrainzTrack); } catch { }
            
            }

            string songartistandname = searchResult.Artists[0] + "_" + searchResult.Name;

            var log = new FileLogging();

            log.LogToMyFile(currentlog, DateTime.Now.ToString("dd.MMM.yyy  -  HH'.'mm'.'ss") + $" [NewDetection] ----------------------------------------------",true);
            log.LogToMyFile(currentlog, DateTime.Now.ToString("dd.MMM.yyy  -  HH'.'mm'.'ss") + $" Song: {searchResult.Name}", true);
            log.LogToMyFile(currentlog, DateTime.Now.ToString("dd.MMM.yyy  -  HH'.'mm'.'ss") + $" Artist: {trackartist}", true);
            log.LogToMyFile(currentlog, DateTime.Now.ToString("dd.MMM.yyy  -  HH'.'mm'.'ss") + $" songid: {songid}", true);
            log.LogToMyFile(currentlog, DateTime.Now.ToString("dd.MMM.yyy  -  HH'.'mm'.'ss") + $" songartistandname: {songartistandname}", true);

            var lyrics = await lyricsFetcher.FetchLyrics(songid);
            var lyricsPath = Path.ChangeExtension(searchResult.Path, ".lyrics.txt");

            await fileSystem.WriteAllTextAsync(lyricsPath, lyrics);
            var result = new MetadataResult<Audio>
            {
                Item = searchResult
            };
            return result;
        }

        public string Name => "LyricsMetadataProvider";

        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<MetadataResult<Audio>> GetMetadata(SongInfo info, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SongInfo searchInfo, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }
}

..

namespace LyricsProvider
{
    public class LyricsFetcher
    {
        private readonly string _apiUrl;

        public LyricsFetcher(string apiUrl)
        {
            _apiUrl = apiUrl;
        }

        public async Task<string> FetchLyrics(string songId)
        {
            using (var httpClient = new HttpClient())
            {
                string apiUrl = $"{_apiUrl}/{songId}/lyrics";
                var response = await httpClient.GetAsync(apiUrl);

                if (response.IsSuccessStatusCode)
                {
                    return await response.Content.ReadAsStringAsync();
                }
                else
                {
                    throw new Exception("Unable to fetch lyrics.");
                }
            }
        }
    }
}

..

namespace LyricsProvider
{
    public class RemoteLyrics
    {
        public string Lyrics { get; set; }
    }
}

I am getting simple injector errors

Quote

SimpleInjector.ActivationException: SimpleInjector.ActivationException: No registration for type LyricsMetadataProvider could be found and an implicit registration could not be made. The constructor of type LyricsMetadataProvider contains the parameter with name 'lyricsFetcher' and type LyricsFetcher, but LyricsFetcher is not registered. For LyricsFetcher to be resolved, it must be registered in the container.
     ---> SimpleInjector.ActivationException: The constructor of type LyricsMetadataProvider contains the parameter with name 'lyricsFetcher' and type LyricsFetcher, but LyricsFetcher is not registered. For LyricsFetcher to be resolved, it must be registered in the container.
     

So either I am doing it wrong or its not supported.

Can you tell me either way if this is wrong or not supported?

Thanks

Posted

Hi, actually, even easier, get rid of that and create a class that implements MediaBrowser.Controller.Subtitles.ISubtitleProvider. Have the SupportedMediaTypes property indicate audio.

Since lyrics and subtitles are essentially the same thing, some placeholders were added to the server core to accommodate this in the future.

Then in theory a lyrics config section should show up in library settings, but this has never been tested because there aren't any providers yet.

mickle026
Posted (edited)

I can add the many thousands of LRC I have to my own api server, let alone the plain text ones

I alreay use ": ILocalMetadataProvider<Audio>" to effictively act like its reading local files but instead goes online and downloads them (kindof a hack), but I want to use a proper remote provider.

So something like this?

And what would trigger it?, Would VideoContentType interface work with Audio files?

public class LyricProvider : ISubtitleProvider
    {

        public string Name => "Music Lyrics";

        IEnumerable<VideoContentType> ISubtitleProvider.SupportedMediaTypes
        {
            get
            {
                yield return VideoContentType.Audio;
            }
        }

        public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IEnumerable<RemoteSubtitleInfo>> Search(SubtitleSearchRequest request, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }

 

Edited by mickle026
Posted

Yes just like that. The subtitle download scheduled task, hopefully. It hasn't been tested.

  • Thanks 1
mickle026
Posted (edited)

I will try this / test this when i have a moment of spare time, but as a scheduled task though? - just why?

At the moment a metadata refresh is doing this job for me with a local reader so when I add a track or album, the lookup of an lrc file is already happening - I think prefer this *hack* method, a lot less stress of the system.

 

reader.jpg.8d7279f9877dd8e4937605ca8e6f4ae9.jpg

Edited by mickle026
Posted

No that’s user controllable in library settings. You should do it the way I mentioned. It will cut the size of your code in half easily.

mickle026
Posted

I will try it with the subtitle interface, and as a standalone task.  I have published it with this method anyway to gauge interest.
 

10 hours ago, Luke said:

No that’s user controllable in library settings. You should do it the way I mentioned. It will cut the size of your code in half easily.

My code is tiny anyway with the provider method - its also pretty fast (apart from the ffprobe element), the only reason the dll is 92kb is because I embeded an icon in the html as base64..

However at even 1 second per track, 10,000 tracks would add 2 and 3/4 hours to a library scan (without the probing) so I can see why you recomend that method.  I see on the forum that some people have massive libraries which could be 200,000+ tracks, which would add over 2 days to the scan.....and hammer the api provider (although text files are much smaller than images so not so much traffic, but rather cpu load on their server)

A scheduled task would be best, I could even do it without the subtitle interface so it runs seperate from the subtitles.  The reason I wanted it as a provider really was so that it triggered for new media when new media was added.

Posted
Quote

so I can see why you recomend that method

And also because users can use the core UI library settings, and down the road when there are other lyrics providers you won't have users asking why yours is different.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...