jellyfin / jellyfin-plugin-opds

GNU General Public License v3.0
27 stars 4 forks source link

Cannot search for books or authors with KOReader #40

Closed rluetzner closed 2 months ago

rluetzner commented 2 months ago

I noticed the following error when using KOReader to access the OPDS feed.

It shows me the option to search the OPDS library, but fails with an exception in Jellyfin.

According to my reverse proxy, the request looks like this:

"HEAD /opds/search?query=stuff HTTP/1.1" 405
"GET /opds/search?query=stuff HTTP/1.1" 400

405 and 400 are the return codes.

Here's the exception that I get from Jellyfin:

jellyfin  | [07:18:02] [ERR] [30] Jellyfin.Api.Middleware.ExceptionMiddleware: Error processing request. URL GET /opds/search.
jellyfin  | System.ArgumentNullException: Value cannot be null. (Parameter 'searchTerm')
jellyfin  |    at System.ArgumentNullException.Throw(String paramName)
jellyfin  |    at System.ArgumentNullException.ThrowIfNull(Object argument, String paramName)
jellyfin  |    at System.ArgumentException.ThrowNullOrEmptyException(String argument, String paramName)
jellyfin  |    at System.ArgumentException.ThrowIfNullOrEmpty(String argument, String paramName)
jellyfin  |    at Emby.Server.Implementations.Library.SearchEngine.GetSearchHints(SearchQuery query, User user)
jellyfin  |    at Emby.Server.Implementations.Library.SearchEngine.GetSearchHints(SearchQuery query)
jellyfin  |    at Jellyfin.Plugin.Opds.Services.OpdsFeedProvider.SearchBooks(String baseUrl, Guid userId, String searchTerm)
jellyfin  |    at Jellyfin.Plugin.Opds.OpdsApi.SearchBookFromQuery(String searchTerms)
jellyfin  |    at lambda_method2365(Closure, Object)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
jellyfin  | --- End of stack trace from previous location ---
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
jellyfin  | --- End of stack trace from previous location ---
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
jellyfin  |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
jellyfin  |    at Jellyfin.Api.Middleware.ServerStartupMessageMiddleware.Invoke(HttpContext httpContext, IServerApplicationHost serverApplicationHost, ILocalizationManager localizationManager)
jellyfin  |    at Jellyfin.Api.Middleware.WebSocketHandlerMiddleware.Invoke(HttpContext httpContext, IWebSocketManager webSocketManager)
jellyfin  |    at Jellyfin.Api.Middleware.IPBasedAccessValidationMiddleware.Invoke(HttpContext httpContext, INetworkManager networkManager)
jellyfin  |    at Jellyfin.Api.Middleware.LanFilteringMiddleware.Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager)
jellyfin  |    at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
jellyfin  |    at Jellyfin.Api.Middleware.QueryStringDecodingMiddleware.Invoke(HttpContext httpContext)
jellyfin  |    at Swashbuckle.AspNetCore.ReDoc.ReDocMiddleware.Invoke(HttpContext httpContext)
jellyfin  |    at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
jellyfin  |    at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
jellyfin  |    at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
jellyfin  |    at Jellyfin.Api.Middleware.RobotsRedirectionMiddleware.Invoke(HttpContext httpContext)
jellyfin  |    at Jellyfin.Api.Middleware.LegacyEmbyRouteRewriteMiddleware.Invoke(HttpContext httpContext)
jellyfin  |    at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionMiddleware.InvokeCore(HttpContext context)
jellyfin  |    at Jellyfin.Api.Middleware.ResponseTimeMiddleware.Invoke(HttpContext context, IServerConfigurationManager serverConfigurationManager)
jellyfin  |    at Jellyfin.Api.Middleware.ExceptionMiddleware.Invoke(HttpContext context)

It looks like the query parameter does not have the expected name searchTerm. Seeing as KOReader works perfectly well with other OPDS sources, I suspect that it's a bug in this plugin.


Thanks for all the hard work. I'll see if I can turn up some more information once I get to a real PC. 🙂

rluetzner commented 2 months ago

Huh, interesting. According to the OPDS specs, the query parameter should actually be called q instead.

https://specs.opds.io/opds-1.2#3-search

Here are some example searches.

With such a template, an OPDS Client could for example support the following search queries:

http://example.com/search?q=gardening http://example.com/search?q=gardening&author=smith http://example.com/search?author=drucker http://example.com/search?author=ferriss&title=four

The best defensive option would be to look for either q or query in the request. I'll see if I can get this done later.