jensenkd / plex-api

.NET Core SDK for Plex Media Server
MIT License
86 stars 27 forks source link

account.Servers(); only returns remote servers you can connect to no local ones #60

Closed seertenedos closed 1 year ago

seertenedos commented 2 years ago

Describe the bug All plex servers have multiple addresses. Normally one remote address and 1+ local addresses that are just IPs an example from my server is "192.168.50.15,192.168.64.1,192.168.128.1,192.168.160.1,192.168.50.16" for the value of LocalAddresses on the account.ServerSummaries() record. Sadly your library never tries to connect using these IP's which should be the fastest and in some cases only access to the server.

To Reproduce Steps to reproduce the behavior:

  1. Have a server on your local network that can not be accessed by your public IP from your network
  2. run account.Servers();
  3. Notice it could not connect to the server as it only tried the public address.

Expected behavior I would expect it to connect using local IP's first then the public one if those fail. One thing to be aware of is you may need to disable the HTTPS cert verification when connecting via local IP's

old logic i used.txt The above attachment is the old code i used to fix this with an older copy of your code that i hope may be helpful

seertenedos commented 2 years ago

I thought about adding my logic to an extension method but that was not possible as the constructor for your Server object needs clients not exposed on your PlexAccount object.

seertenedos commented 2 years ago

I have worked out a little more info. It seems like you can use a valid https cert and the ip address usinghttps://plex.tv/api/v2/resources?includeHttps=1&includeRelay=1&includeIPv6=1&X-Plex-Token=xxxxxxxxx&X-Plex-Client-Identifier=xxxxxxxxxxx it returns as part of the result all the https urls for the servers both internal and external. That would likely be the ideal way to get a list of connections options for the servers and then test each connection to see the one to use. You may be able to include the "includeHttps=1" option with what ever your current query is and it may work

seertenedos commented 2 years ago

here is a working version i did in an extension method but would likely be better in the library itself.


        public static async Task<List<Server>> OnlineServers(this PlexAccount account, IPlexServerClient plexServerClient,
            IPlexLibraryClient plexLibraryClient)
        {
            var resources = (await account.Resources()).Resources;
            var serverList = (await account.ServerSummaries()).Servers;
            var serverListAndUrls = serverList.Select(x => new { Server = x, Urls = resources.FirstOrDefault(y=>y.ClientIdentifier == x.MachineIdentifier)?.Connections.Select(z=>z.Uri).ToList()??new () });

            List<Server> servers = new List<Server>();
            foreach (var serverAndUrls in serverListAndUrls)
            {
                try
                {
                    (bool isOnline, string bestUrl) = CheckServerIsOnline(serverAndUrls.Urls);
                    if (!isOnline)
                    {
                        Console.WriteLine($"Cannot access server: {serverAndUrls.Server.Name} on {string.Join(", ", serverAndUrls.Urls)}");
                        continue;
                    }
                    var uri = new Uri(bestUrl);
                    serverAndUrls.Server.Port = uri.Port;
                    serverAndUrls.Server.Host = uri.Host;
                    serverAndUrls.Server.Scheme = uri.Scheme;
                    var server = new Server(plexServerClient, plexLibraryClient, serverAndUrls.Server);
                    servers.Add(server);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Cannot access server: {serverAndUrls.Server.Name} on {string.Join(", ", serverAndUrls.Urls)}. Exception:{ex}");
                }
            }
            return servers;
        }

        private static (bool IsOnline, string bestUrl) CheckServerIsOnline(List<string> urls)
        {
            var results = new ConcurrentDictionary<string, TimeSpan>();
            Parallel.ForEach(urls, url =>
            {
                var sw = new Stopwatch();
                sw.Start();
                var isOnline = Helper.IsPlexServerOnline(url).Result;
                sw.Stop();
                if (isOnline)
                {
                    results[url] = sw.Elapsed;
                }
            });

            if (results.Count == 0)
            {
                return (false, urls.FirstOrDefault());
            }
            else
            {
                return (true, results.MinBy(x => x.Value).Key);
            }
        }
jensenkd commented 2 years ago

Thanks @seertenedos , I'll take a look

Pouleyy commented 2 years ago

I've come to the problem where my server won't display account.Servers() too. That's mostly why I went for the Client libraries method instead of account method. My problem was quite similar. It's only a local server but exposed to the world via reverse proxy, so never really exposed to the world for Plex unfortunatly.

My problem is that my server isn't on the plex.tv/pms/servers.xml API, but is on the plex.tv/api/v2/resources route. Won't it be easier and more reliable to just call plex.tv/api/v2/resources to get the list of server (using provides="server" to extract server) ? It seems that this route do everthing as the other one and more. It'll fix @seertenedos problem for local server and at the same time fix my problem about no server on /pms/servers.xml

What do you think about that @jensenkd ? I can work on that if you want!

seertenedos commented 2 years ago

@Pouleyy That is strange it is not on /pms/servers.xml. That should include the local servers not just ones exposed to the internet. The account.Servers() was filtering based on the public address but i did not think account.ServerSummaries() did as all of mine were in there. My fix used both together to add the extra details from resources to the server but my servers were listed by account.ServerSummaries() it was just that most of the connection details were not there that i needed including the proper url's to conenct when the server is SSL only server.

Pouleyy commented 2 years ago

Yes, quite strange, I’ve got nothing on /pms/servers.xml But everything appears on /v2/ressources Yep your fix was just to adjust URL of account.Servers() but as /v2/ressources provide everything we need, it might be better to be just based on that First try connecting to connection marked as local to retrieve the URL then try the non local one So in this case as I said it’ll fix your problem, mine, and we’ll have the same behavior as before I guess🤔

seertenedos commented 2 years ago

That could work. Really interesting that your server is not in the server list. Anything on the resource list that may hint why?

On Thu, 19 May 2022, 9:10 am Kevin Harismendy, @.***> wrote:

Yes, quite strange, I’ve got nothing on /pms/servers.xml But everything appears on /v2/ressources Yep your fix was just to adjust URL of account.Servers() but as /v2/ressources provide everything we need, it might be better to be just based on that First try connecting to connection marked as local to retrieve the URL then try the non local one So in this case as I said it’ll fix your problem, mine, and we’ll have the same behavior as before I guess🤔

— Reply to this email directly, view it on GitHub https://github.com/jensenkd/plex-api/issues/60#issuecomment-1130699200, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADB44JRBRY4BKANENC4GX3VKV2ENANCNFSM5WGL65VA . You are receiving this because you were mentioned.Message ID: @.***>

Pouleyy commented 2 years ago

Yes it's really weird, I don't really know what might do that. Even not showing with it local adress on /pms/servers.xml 🤷‍♂️ As I said maybe to my uncommon installation but still doesn't explain lack of local adress 😕