Playback controls missing from downloaded podcasts

Post your Server Bug Report
User avatar
troycarpenter
Posts: 138
Joined: 03 Dec 2013, 19:16
Has thanked: 28 times
Been thanked: 50 times

Playback controls missing from downloaded podcasts

Unread post by troycarpenter »

I don't know if playback controls is the right term, but here's the problem:

I currently have 5 active podcasts in my library. In the list of episodes, if an episode has been downloaded, there is usually a set controls to the left of the episode that allow for "Play", "Play Next", and "Play Last". I've noticed that as time goes on (especially after a screen refresh), those play controls disappear, and make it impossible to play the podcasts in the web interface.

Below is a screenshot showing an entry where there are no controls, even when the episode is marked as "complete", which I take means it's been downloaded, as opposed to "skipped".
podcast-1.JPG
podcast-1.JPG (11.38 KiB) Viewed 2998 times
Here's an example of an entry that still has the playback controls:
podcast-2.JPG
podcast-2.JPG (11.12 KiB) Viewed 2998 times
In the various clients (mobile or even Jamstash), the episodes are able to be played.

EDIT: It looks like the DB gets out of whack with reality when the download gets aborted for any reason. Even if the download fails, the database gets updated to show the download succeeded. It also seems like the controls only show up when the file is physically there instead of based on the database, but download decisions are only made based on the status in the database, meaning the two can get out of sync.
User avatar
troycarpenter
Posts: 138
Joined: 03 Dec 2013, 19:16
Has thanked: 28 times
Been thanked: 50 times

Re: Playback controls missing from downloaded podcasts

Unread post by troycarpenter »

I think the code must check to see if the episode actually exists on disk before adding the playback controls.

I would suggest that when that check is made, and the episode doesn't exist on disk, then the STATUS field in the database should be changed back to "SKIPPED". This needs to be done because once the database says that an episode is "COMPLETE", then it will not try to download it again later. It needs to be in the "SKIPPED" state before Madsonic tries to download again.

Another solution would be to leave the status as is, but allow Madsonic to try to download the episode again if requested by the user (or by the number of episodes policy). If the episode doesn't exist on disk, then it will be re-downloaded. If it already exists, then it will be skipped.

I actually prefer the first method, though.
These users thanked the author troycarpenter for the post:
Madsonic
Rating: 7.69%
User avatar
troycarpenter
Posts: 138
Joined: 03 Dec 2013, 19:16
Has thanked: 28 times
Been thanked: 50 times

Re: Playback controls missing from downloaded podcasts

Unread post by troycarpenter »

Got into a situation again today where I had podcast episodes downloaded and marked as "COMPLETE" in the podcast episode database, but there were no playback controls. When I tried to play one of those episodes from my mobile client, the server spit out this log:

Code: Select all

[2017-02-09 14:33:36,414] WARN RESTMadsonicFilter - Error in REST API layer: PreparedStatementCallback; SQL [insert into play_queue_file(sequence_id, play_queue_id, media_file_id) values (?, ?, ?)]; integrity constraint violation: foreign key no parent; SYS_FK_10794 table: PLAY_QUEUE_FILE; nested exception is java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no parent; SYS_FK_10794 table: PLAY_QUEUE_FILE
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into play_queue_file(sequence_id, play_queue_id, media_file_id) values (?, ?, ?)]; integrity constraint violation: foreign key no parent; SYS_FK_10794 table: PLAY_QUEUE_FILE; nested exception is java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no parent; SYS_FK_10794 table: PLAY_QUEUE_FILE
        at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:86)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:605)
        at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:818)
        at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:874)
        at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:882)
        at org.madsonic.dao.PlayQueueDao.savePlayQueue(PlayQueueDao.java:63)
        at org.madsonic.controller.REST.Madsonic.RESTController.savePlayQueue(RESTController.java:2512)
        at sun.reflect.GeneratedMethodAccessor198.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.servlet.mvc.multiaction.MultiActionController.invokeNamedMethod(MultiActionController.java:471)
        at org.springframework.web.servlet.mvc.multiaction.MultiActionController.handleRequestInternal(MultiActionController.java:408)
        at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
        at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:933)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:867)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:951)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:842)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:735)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:827)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1507)
        at org.eclipse.jetty.servlets.UserAgentFilter.doFilter(UserAgentFilter.java:82)
        at org.eclipse.jetty.servlets.GzipFilter.doFilter(GzipFilter.java:294)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1495)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.madsonic.security.MadsonicRESTRequestParameterProcessingFilter.doFilter(MadsonicRESTRequestParameterProcessingFilter.java:111)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1495)
        at org.madsonic.filter.RequestEncodingFilter.doFilter(RequestEncodingFilter.java:44)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1495)
        at org.madsonic.filter.RESTMadsonicFilter.doFilter(RESTMadsonicFilter.java:63)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1495)
        at org.madsonic.filter.ParameterDecodingFilter.doFilter(ParameterDecodingFilter.java:63)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1495)
        at org.madsonic.filter.BootstrapVerificationFilter.doFilter(BootstrapVerificationFilter.java:60)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1487)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:499)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:557)
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:427)
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
        at org.eclipse.jetty.server.Server.handle(Server.java:370)
        at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
        at org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53)
        at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:973)
        at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:1035)
        at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:641)
        at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:231)
        at org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72)
        at org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264)
        at org.eclipse.jetty.server.ssl.SslSocketConnector$SslConnectorEndPoint.run(SslSocketConnector.java:670)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
        at java.lang.Thread.run(Thread.java:745)
Caused by: java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no parent; SYS_FK_10794 table: PLAY_QUEUE_FILE
        at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
        at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
        at org.hsqldb.jdbc.JDBCPreparedStatement.fetchResult(Unknown Source)
        at org.hsqldb.jdbc.JDBCPreparedStatement.executeUpdate(Unknown Source)
        at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:824)
        at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:818)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:589)
        ... 77 more
Caused by: org.hsqldb.HsqlException: integrity constraint violation: foreign key no parent; SYS_FK_10794 table: PLAY_QUEUE_FILE
        at org.hsqldb.error.Error.error(Unknown Source)
        at org.hsqldb.Constraint.getException(Unknown Source)
        at org.hsqldb.Constraint.checkInsert(Unknown Source)
        at org.hsqldb.StatementDML.performIntegrityChecks(Unknown Source)
        at org.hsqldb.StatementDML.insertSingleRow(Unknown Source)
        at org.hsqldb.StatementInsert.getResult(Unknown Source)
        at org.hsqldb.StatementDMQL.execute(Unknown Source)
        at org.hsqldb.Session.executeCompiledStatement(Unknown Source)
        at org.hsqldb.Session.execute(Unknown Source)
        ... 82 more
The episode does NOT play in the player and says "buffering 100%".

I have to delete the podcast channel (or delete all the podcast episodes in the database for that channel), delete the downloaded episodes from disk, then re-add or check for new episodes (depending on what I did).
User avatar
troycarpenter
Posts: 138
Joined: 03 Dec 2013, 19:16
Has thanked: 28 times
Been thanked: 50 times

Re: Playback controls missing from downloaded podcasts

Unread post by troycarpenter »

This is still happening quite often. Today, all my podcasts had no play controls in the web interface even though they all said "complete" in the status. I had to delete all the episodes from the database, and delete all the recordings on disk in order to restore functionality. I don't know why I have to delete the physical files, but many times the play controls don't show up if I don't delete the files...maybe a bug when the file is requested to be downloaded but the file already exists.
Post Reply