Schaka / janitorr

Cleans your Radarr, Sonarr, Jellyseerr and Jellyfin before you run out of space
GNU General Public License v3.0
313 stars 7 forks source link

401 Unauthorized during TV show DELETE #52

Closed mihaif7 closed 2 months ago

mihaif7 commented 2 months ago

Hello, been using Janitorr for a while now but I noticed that I get a 401 Error when trying to delete TV shows/seasons.

Here's the error message it's showing in the container:

2024-08-15T07:44:41.577Z ERROR 1 --- [   scheduling-1] c.g.s.j.m.AbstractMediaServerRestService : Deleting Sex Education Season 4 from Jellyfin

feign.FeignException$Unauthorized: [401 Unauthorized] during [DELETE] to [http://192.168.1.106:8096/Items/e950c6a01029b33afe4980c2758c2210] [MediaServerUserClient#deleteItemAndFiles(String)]: ["Unauthorized access"]
    at feign.FeignException.clientErrorStatus(FeignException.java:224) ~[feign-core-13.1.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:203) ~[feign-core-13.1.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:194) ~[feign-core-13.1.jar:na]
    at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:103) ~[feign-core-13.1.jar:na]
    at feign.InvocationContext.decodeError(InvocationContext.java:126) ~[feign-core-13.1.jar:na]
    at feign.InvocationContext.proceed(InvocationContext.java:72) ~[feign-core-13.1.jar:na]
    at feign.ResponseHandler.handleResponse(ResponseHandler.java:63) ~[feign-core-13.1.jar:na]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:114) ~[feign-core-13.1.jar:na]
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:70) ~[feign-core-13.1.jar:na]
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:99) ~[feign-core-13.1.jar:na]
    at jdk.proxy2/jdk.proxy2.$Proxy82.deleteItemAndFiles(Unknown Source) ~[na:na]
    at com.github.schaka.janitorr.mediaserver.AbstractMediaServerRestService.cleanupTvShows(AbstractMediaServerRestService.kt:65) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.deleteTvShows(AbstractCleanupSchedule.kt:102) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule$scheduleDelete$2.invoke(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule$scheduleDelete$2.invoke(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.cleanupMediaType(AbstractCleanupSchedule.kt:76) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.scheduleDelete(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.scheduleDelete$default(AbstractCleanupSchedule.kt:33) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.MediaCleanupSchedule.runSchedule(MediaCleanupSchedule.kt:52) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheInterceptor.lambda$invoke$0(CacheInterceptor.java:64) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:416) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.evaluate(CacheAspectSupport.java:548) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:433) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:395) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:74) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:720) ~[spring-aop-6.1.11.jar:6.1.11]
    at com.github.schaka.janitorr.cleanup.MediaCleanupSchedule$$SpringCGLIB$$0.runSchedule(<generated>) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) ~[spring-context-6.1.11.jar:6.1.11]
    at io.micrometer.observation.Observation.observe(Observation.java:499) ~[micrometer-observation-1.13.2.jar:1.13.2]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-6.1.11.jar:6.1.11]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.runAndReset(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) ~[na:na]
    at java.base/java.lang.VirtualThread.run(Unknown Source) ~[na:na]

2024-08-15T07:44:41.585Z ERROR 1 --- [   scheduling-1] c.g.s.j.m.AbstractMediaServerRestService : Deleting The Continental: From the World of John Wick Season 1 from Jellyfin

feign.FeignException$Unauthorized: [401 Unauthorized] during [DELETE] to [http://192.168.1.106:8096/Items/3103e9f652632ecde1652e796f5b3c3c] [MediaServerUserClient#deleteItemAndFiles(String)]: ["Unauthorized access"]
    at feign.FeignException.clientErrorStatus(FeignException.java:224) ~[feign-core-13.1.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:203) ~[feign-core-13.1.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:194) ~[feign-core-13.1.jar:na]
    at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:103) ~[feign-core-13.1.jar:na]
    at feign.InvocationContext.decodeError(InvocationContext.java:126) ~[feign-core-13.1.jar:na]
    at feign.InvocationContext.proceed(InvocationContext.java:72) ~[feign-core-13.1.jar:na]
    at feign.ResponseHandler.handleResponse(ResponseHandler.java:63) ~[feign-core-13.1.jar:na]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:114) ~[feign-core-13.1.jar:na]
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:70) ~[feign-core-13.1.jar:na]
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:99) ~[feign-core-13.1.jar:na]
    at jdk.proxy2/jdk.proxy2.$Proxy82.deleteItemAndFiles(Unknown Source) ~[na:na]
    at com.github.schaka.janitorr.mediaserver.AbstractMediaServerRestService.cleanupTvShows(AbstractMediaServerRestService.kt:65) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.deleteTvShows(AbstractCleanupSchedule.kt:102) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule$scheduleDelete$2.invoke(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule$scheduleDelete$2.invoke(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.cleanupMediaType(AbstractCleanupSchedule.kt:76) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.scheduleDelete(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.scheduleDelete$default(AbstractCleanupSchedule.kt:33) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.MediaCleanupSchedule.runSchedule(MediaCleanupSchedule.kt:52) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheInterceptor.lambda$invoke$0(CacheInterceptor.java:64) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:416) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.evaluate(CacheAspectSupport.java:548) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:433) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:395) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:74) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:720) ~[spring-aop-6.1.11.jar:6.1.11]
    at com.github.schaka.janitorr.cleanup.MediaCleanupSchedule$$SpringCGLIB$$0.runSchedule(<generated>) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) ~[spring-context-6.1.11.jar:6.1.11]
    at io.micrometer.observation.Observation.observe(Observation.java:499) ~[micrometer-observation-1.13.2.jar:1.13.2]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-6.1.11.jar:6.1.11]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.runAndReset(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) ~[na:na]
    at java.base/java.lang.VirtualThread.run(Unknown Source) ~[na:na]

2024-08-15T07:44:41.588Z ERROR 1 --- [   scheduling-1] c.g.s.j.m.AbstractMediaServerRestService : Deleting Chernobyl Season 1 from Jellyfin

feign.FeignException$Unauthorized: [401 Unauthorized] during [DELETE] to [http://192.168.1.106:8096/Items/45583a7d63e4936a7316df8739199efd] [MediaServerUserClient#deleteItemAndFiles(String)]: ["Unauthorized access"]
    at feign.FeignException.clientErrorStatus(FeignException.java:224) ~[feign-core-13.1.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:203) ~[feign-core-13.1.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:194) ~[feign-core-13.1.jar:na]
    at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:103) ~[feign-core-13.1.jar:na]
    at feign.InvocationContext.decodeError(InvocationContext.java:126) ~[feign-core-13.1.jar:na]
    at feign.InvocationContext.proceed(InvocationContext.java:72) ~[feign-core-13.1.jar:na]
    at feign.ResponseHandler.handleResponse(ResponseHandler.java:63) ~[feign-core-13.1.jar:na]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:114) ~[feign-core-13.1.jar:na]
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:70) ~[feign-core-13.1.jar:na]
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:99) ~[feign-core-13.1.jar:na]
    at jdk.proxy2/jdk.proxy2.$Proxy82.deleteItemAndFiles(Unknown Source) ~[na:na]
    at com.github.schaka.janitorr.mediaserver.AbstractMediaServerRestService.cleanupTvShows(AbstractMediaServerRestService.kt:65) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.deleteTvShows(AbstractCleanupSchedule.kt:102) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule$scheduleDelete$2.invoke(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule$scheduleDelete$2.invoke(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.cleanupMediaType(AbstractCleanupSchedule.kt:76) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.scheduleDelete(AbstractCleanupSchedule.kt:49) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.AbstractCleanupSchedule.scheduleDelete$default(AbstractCleanupSchedule.kt:33) ~[classes/:na]
    at com.github.schaka.janitorr.cleanup.MediaCleanupSchedule.runSchedule(MediaCleanupSchedule.kt:52) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheInterceptor.lambda$invoke$0(CacheInterceptor.java:64) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:416) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.evaluate(CacheAspectSupport.java:548) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:433) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:395) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:74) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768) ~[spring-aop-6.1.11.jar:6.1.11]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:720) ~[spring-aop-6.1.11.jar:6.1.11]
    at com.github.schaka.janitorr.cleanup.MediaCleanupSchedule$$SpringCGLIB$$0.runSchedule(<generated>) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) ~[spring-context-6.1.11.jar:6.1.11]
    at io.micrometer.observation.Observation.observe(Observation.java:499) ~[micrometer-observation-1.13.2.jar:1.13.2]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) ~[spring-context-6.1.11.jar:6.1.11]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-6.1.11.jar:6.1.11]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.runAndReset(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) ~[na:na]
    at java.base/java.lang.VirtualThread.run(Unknown Source) ~[na:na]

I use docker-compose for everything. My docker-compose looks something like this

version: "3"
services:
  janitorr:
    container_name: janitorr
    image: ghcr.io/schaka/janitorr:latest
    volumes:
      - /media/storage/Configs/Janitorr:/config
      - /media/library:/data
      - /media/library/:/storage
    restart: unless-stopped
networks: {}

Directories are mapped the same in the *arr stack and in Jellyfin. The Jellyfin config in application.yml is configured with the correct values (user + password works and api key shows up in Jellyfin)

server:
  port: 8978

# File system access (same mapping as Sonarr, Radarr and Jellyfin) is required to delete TV shows by season and create "Leaving Soon" collections in Jellyfin
# Currently, Jellyfin does not support an easy way to add only a few seasons or movies to a collection, we need access to temporary symlinks
# Additionally, checks to prevent deletion on currently still seeding media currently require file system access as well
file-system:
  access: true
  validate-seeding: true # validates seeding by checking if the original file exists and skips deletion - turning this off will send a delete to the *arrs even if a torrent may still be active
  leaving-soon-dir: "/storage/leaving-soon" # A directory this container can write to and Jellyfin can find under the same path - this will contain new folders with symlinks to files for Jellyfin's "Leaving Soon" collections
  from-scratch: true # Clean up entire "Leaving Soon" directory and rebuild from scratch - this can help with clearing orphaned data - turning this off can save resources (less writes to drive)
  free-space-check-dir: "/data" # This is the default directory Janitorr uses to check how much space is left on your drives. By default, it checks the entire root - you may point it at a specific folder

application:
  dry-run: false
  whole-tv-show: true # activating this will treat as a whole show as recently download/watched from a single episode, rather than that episode's season - shows will be deleted as a whole
  whole-show-seeding-check: true # Turning this off, disables the seeding check entirely if whole-tv-show is enabled. Activating this check will keep a whole TV show if any season is still seeding (requires file access).
  leaving-soon: 14d # 14 days before a movie is deleted, it gets added to a "Leaving Soon" type collection (i.e. movies that are 76 to 89 days old)
  exclusion-tag: "janitorr_keep" # Set this tag to your movies or TV shows in the *arrs to exclude media from being cleaned up

  media-deletion:
    enabled: true
    movie-expiration:
      # Percentage of free disk space to expiration time - if the highest given number is not reached, nothing will be deleted
      # If filesystem access is not given, disk percentage can't be determined. As a result, Janitorr will always choose the largest expiration time.
      5: 182d # 15 days
      10: 192d # 1 month - if a movie's files on your system are older than this, they will be deleted
      15: 202d # 2 months
      20: 300d # 3 months
    season-expiration:
      5: 182d # 15 days
      10: 192d # 20 days - if a season's files on your system are older than this, they will be deleted
      15: 202d # 2 months
      20: 300d # 4 months

  tag-based-deletion:
    enabled: true
    minimum-free-disk-percent: 100
    schedules:
      - tag: 5 - demo
        expiration: 30d
      - tag: 10 - demo
        expiration: 7d

  episode-deletion: # This ignores Jellystat. Only grab history matters. It also doesn't clean up Jellyfin. There is NO seeding check either.
    enabled: false
    tag: janitorr_daily # Shows tagged with this will have all episodes of their LATEST season deleted by the below thresholds
    max-episodes: 10 # maximum (latest) episodes of this season to keep
    max-age: 30d # Maximum age to keep any episode at all - even the last 10 episodes would expire after 30 days in this example

clients:
  sonarr:
    enabled: true
    url: "<sonarr_url>"
    api-key: "<api_key>"
    delete-empty-shows: true # If a show that was "touched" by Janitorr contains no files and has no monitored seasons at all, it will get deleted as part of orphan cleanup
  radarr:
    enabled: true
    url: "<radarr_url>"
    api-key: "<api_key>"

  ## You can only choose one out of Jellyfin or Emby.
  ## User login is only needed if deletion is enabled.
  jellyfin:
    enabled: true
    url: "<jellyfin_url>"
    api-key: "<api_key>"
    username: <cleanup_user>
    password: <cleanup_password>
    delete: true # Jellyfin setup is required for JellyStat. However, if you don't want Janitorr to send delete requests to the Jellyfin API, disable it here

  jellyseerr:
    enabled: true
    url: "<jellyseer_url>"
    api-key: "<api_key>"
    match-server: false # Enable if you have several Radarr/Sonarr instances set up in Jellyseerr. Janitorr will match them by the host+port supplied in their respective config settings.
  jellystat:
    enabled: true
    whole-tv-show: true # Enabling this will make Jellystat consider TV shows as a whole if any episode of any season has been watched
    url: "<jellystat_url>"
    api-key: "<api_key>"

logging:
  level:
    com.github.schaka: TRACE

Not sure if this is a bug or I missconfigured something.

Thanks for all the work on this amazing project :)

Schaka commented 2 months ago

I assume the user you're using for Jellyfin to delete assets (this is required) has the correct access to the library it's trying to delete from?

I would double check your permissions. Nobody else has reported anything like it and I've been running my own for months without issues like that.

I'd also recommend getting off the latest tag and at least use stable or native-stable, depending on what you prefer.

mihaif7 commented 2 months ago

The user that I'm using for the cleanup has the following permissions and also has access to all libraries. image

Am I missing something? Because it's only happening for TV shows, movies get deleted just fine

PS: Changed from latest to stable.

mihaif7 commented 2 months ago

Ok managed to fix the issue but not sure what did the fix.

I changed the leaving-soon directories ownership to be the same folder as the Movies and TV folders and I also provided the PUID and the PGID as env variables to the Janitorr container. Was thrown off by the 401 thrown by Jellyfin and assumed that it was an Issue with the user/permission in Jellyfin.

Anyway thanks for the help

Schaka commented 2 months ago

Are you using whole-show or season by season?

Can you confirm the ID above in the error log exists. It says: Items/45583a7d63e4936a7316df8739199efd

When you open your server as your Jellyfin user and go to a random TV show or season, the URl should look like: https://jellyfin.yours/web/#/details?id=900500dc5bf2dbd2316b0b213960afd4&serverId=ba136f1bd03e473b8cedfcf910527b30

If you exchange 900500dc5bf2dbd2316b0b213960afd4 with 45583a7d63e4936a7316df8739199efd, what does it show? Can you see this item and can you delete it?

It could technically happen that Janitorr deletes an item via Sonarr and Jellyfin picks up on this deletion SO quickly via directory scan, that before Janitorr even sends the delete request to Jellyfin, it's already gone. This is unlikely and has never happened to me, but I can imagine it could technically happen.

Schaka commented 2 months ago

Was thrown off by the 401 thrown by Jellyfin and assumed that it was an Issue with the user/permission in Jellyfin.

I suspect that Janitorr created a the subfolders which it then told Jellyfin to use. If Jelly tried to delete something inside the Leaving Soon library that was created physically on disk by Janitorr but cannot be accessed by Jellyfin, Jellyfin will throw a 401 apparently.

I've not seen that before, but I think it's time to start a wiki/FAQ with these "common errors" and their solutions. So thank you a lot of reporting and reporting the solution too.

mihaif7 commented 2 months ago

Are you using whole-show or season by season?

Can you confirm the ID above in the error log exists. It says: Items/45583a7d63e4936a7316df8739199efd

When you open your server as your Jellyfin user and go to a random TV show or season, the URl should look like: https://jellyfin.yours/web/#/details?id=900500dc5bf2dbd2316b0b213960afd4&serverId=ba136f1bd03e473b8cedfcf910527b30

If you exchange 900500dc5bf2dbd2316b0b213960afd4 with 45583a7d63e4936a7316df8739199efd, what does it show? Can you see this item and can you delete it?

It could technically happen that Janitorr deletes an item via Sonarr and Jellyfin picks up on this deletion SO quickly via directory scan, that before Janitorr even sends the delete request to Jellyfin, it's already gone. This is unlikely and has never happened to me, but I can imagine it could technically happen.

Unfortunately I already cleaned everything up and didn't think to check if the files were still working in Jellyfin, but I suspect they were still accessible. The shows in question should have been deleted directly since they were way above the threshold set in Janitorr. And to make things worse for Movies it worked without an Issue.

Movies above threshold -> deleted TV Shows above threshold -> error

Was thrown off by the 401 thrown by Jellyfin and assumed that it was an Issue with the user/permission in Jellyfin.

I suspect that Janitorr created a the subfolders which it then told Jellyfin to use. If Jelly tried to delete something inside the Leaving Soon library that was created physically on disk by Janitorr but cannot be accessed by Jellyfin, Jellyfin will throw a 401 apparently.

I've not seen that before, but I think it's time to start a wiki/FAQ with these "common errors" and their solutions. So thank you a lot of reporting and reporting the solution too.

The items were not in the leaving-soon directory. They were in the TV directory that I've been using for a long time without an issue. Permissions for the files were ok. It fixed itself after I fixed the leaving-soon permissions.

Another thing I noticed was that it added a TV show to the leaving-soon section but the TV show was under the treshold and it only added the seasons without any episodes. It happened a few times. If it happens again I'll get back to you with as much info as I can.

Schaka commented 2 months ago

Another thing I noticed was that it added a TV show to the leaving-soon section but the TV show was under the treshold and it only added the seasons without any episodes. It happened a few times. If it happens again I'll get back to you with as much info as I can.

It could still be some bug with the whole-show option that treats empty Sonarr seasons differently, somehow. If this happens again and you can provide some logs, that'd be great.