From ae663340c912b73940f1ebadb9e258c64e371238 Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 15:44:37 +0100 Subject: [PATCH 01/10] Added debugs --- qBitrr/arss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qBitrr/arss.py b/qBitrr/arss.py index 6fb098ae..13299ba5 100755 --- a/qBitrr/arss.py +++ b/qBitrr/arss.py @@ -1300,7 +1300,7 @@ def db_get_files_series( .order_by(self.series_file_model.EntryId.asc()) .execute() ): - self.logger.debug("Yielding %s", entry_.Title) + self.logger.debug("Should yielding %s", entry_.Title) for entry_ in ( self.series_file_model.select() .where(condition) From 09767614ea64f6f68ded9110a85f6a046e5de1ed Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 15:54:23 +0100 Subject: [PATCH 02/10] Series search fix test --- qBitrr/arss.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/qBitrr/arss.py b/qBitrr/arss.py index 13299ba5..19f97de5 100755 --- a/qBitrr/arss.py +++ b/qBitrr/arss.py @@ -1299,13 +1299,6 @@ def db_get_files_series( .where(condition) .order_by(self.series_file_model.EntryId.asc()) .execute() - ): - self.logger.debug("Should yielding %s", entry_.Title) - for entry_ in ( - self.series_file_model.select() - .where(condition) - .order_by(self.series_file_model.EntryId.asc()) - .execute() ): self.logger.trace("Yielding %s", entry_.Title) yield entry_, False, False @@ -4258,7 +4251,13 @@ def run_search_loop(self) -> NoReturn: self.force_grab() raise RestartLoopException for entry, todays, limit_bypass, series_search in self.db_get_files(): - self.logger.trace("Running search for %s", entry.Title) + self.logger.trace( + "Running search for %s [%s, %s, %s]", + entry.Title, + todays, + limit_bypass, + series_search, + ) while ( self.maybe_do_search( entry, From d916d5e37da2eef87fadbac1eb544c13a9248325 Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 16:02:14 +0100 Subject: [PATCH 03/10] v4 Workflow fix --- .github/workflows/nightlyv4.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightlyv4.yml b/.github/workflows/nightlyv4.yml index 2c4d30f4..e5759113 100644 --- a/.github/workflows/nightlyv4.yml +++ b/.github/workflows/nightlyv4.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - ref: master + ref: v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx From 76cffc25d20294ce9e06920be24f552e5ed1f6b2 Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 16:53:51 +0100 Subject: [PATCH 04/10] Yield rewrite --- qBitrr/arss.py | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/qBitrr/arss.py b/qBitrr/arss.py index 19f97de5..351f6637 100755 --- a/qBitrr/arss.py +++ b/qBitrr/arss.py @@ -1148,15 +1148,17 @@ def db_get_files( tuple[MoviesFilesModel | EpisodeFilesModel | SeriesFilesModel, bool, bool, bool] ]: if self.type == "sonarr" and self.series_search: - for i1, i2, i3 in self.db_get_files_series(): - self.logger.trace("Yielding %s", i1.Title) - yield i1, i2, i3, i3 is not True + for series in self.db_get_files_series(): + for i1, i2, i3 in series: + yield i1, i2, i3, i3 is not True elif self.type == "sonarr" and not self.series_search: - for i1, i2, i3 in self.db_get_files_episodes(): - yield i1, i2, i3, False + for episodes in self.db_get_files_episodes(): + for i1, i2, i3 in episodes: + yield i1, i2, i3, False elif self.type == "radarr": - for i1, i2, i3 in self.db_get_files_movies(): - yield i1, i2, i3, False + for movies in self.db_get_files_movies(): + for i1, i2, i3 in movies: + yield i1, i2, i3, False def db_maybe_reset_entry_searched_state(self): if self.type == "sonarr": @@ -1266,11 +1268,12 @@ def db_reset__movie_searched_state(self): def db_get_files_series( self, - ) -> Iterable[tuple[SeriesFilesModel, bool, bool]]: + ) -> list[tuple(SeriesFilesModel, bool, bool)] | None: + entries = [] if not self.search_missing: - yield None, False, False + return None elif not self.series_search: - yield None, False, False + return None elif self.type == "sonarr": condition = self.model_file.AirDateUtc.is_null(False) if not self.search_specials: @@ -1289,7 +1292,7 @@ def db_get_files_series( for i1, i2, i3 in self._search_todays(condition): if i1 is not None: self.logger.trace("Yielding %s", i1.Title) - yield i1, i2, i3 + entries.append(tuple(i1, i2, i3)) if not self.do_upgrade_search: condition = self.series_file_model.Searched == False else: @@ -1301,13 +1304,15 @@ def db_get_files_series( .execute() ): self.logger.trace("Yielding %s", entry_.Title) - yield entry_, False, False + entries.append(tuple(entry_, False, False)) + return entries def db_get_files_episodes( self, - ) -> Iterable[tuple[EpisodeFilesModel, bool, bool]]: + ) -> list[tuple(EpisodeFilesModel, bool, bool)] | None: + entries = [] if not self.search_missing: - yield None, False, False + return None elif self.type == "sonarr": condition = self.model_file.AirDateUtc.is_null(False) if not self.search_specials: @@ -1360,13 +1365,15 @@ def db_get_files_episodes( has_been_queried = True for i1, i2, i3 in self._search_todays(today_condition): if i1 is not None: - yield i1, i2, i3 + entries.append(tuple(i1, i2, i3)) + return entries def db_get_files_movies( self, - ) -> Iterable[tuple[MoviesFilesModel, bool, bool]]: + ) -> list[tuple(MoviesFilesModel, bool, bool)] | None: + entries = [] if not self.search_missing: - yield None, False, False + return None if self.type == "radarr": condition = self.model_file.Year.is_null(False) if self.search_by_year: @@ -1394,7 +1401,8 @@ def db_get_files_movies( .order_by(self.model_file.Title.asc()) .execute() ): - yield entry, False, False + entries.append(tuple(entry, False, False)) + return entries def db_get_request_files(self) -> Iterable[MoviesFilesModel | EpisodeFilesModel]: if (not self.ombi_search_requests) or (not self.overseerr_requests): From e17c41b6d72faca47fc71e39b5e715deea363ee5 Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 16:57:33 +0100 Subject: [PATCH 05/10] Error fix --- qBitrr/arss.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/qBitrr/arss.py b/qBitrr/arss.py index 351f6637..b685883e 100755 --- a/qBitrr/arss.py +++ b/qBitrr/arss.py @@ -1268,7 +1268,7 @@ def db_reset__movie_searched_state(self): def db_get_files_series( self, - ) -> list[tuple(SeriesFilesModel, bool, bool)] | None: + ) -> list[tuple[SeriesFilesModel, bool, bool]] | None: entries = [] if not self.search_missing: return None @@ -1291,8 +1291,7 @@ def db_get_files_series( ) | self.model_file.SceneAbsoluteEpisodeNumber.is_null(False) for i1, i2, i3 in self._search_todays(condition): if i1 is not None: - self.logger.trace("Yielding %s", i1.Title) - entries.append(tuple(i1, i2, i3)) + entries.append(tuple[i1, i2, i3]) if not self.do_upgrade_search: condition = self.series_file_model.Searched == False else: @@ -1303,13 +1302,12 @@ def db_get_files_series( .order_by(self.series_file_model.EntryId.asc()) .execute() ): - self.logger.trace("Yielding %s", entry_.Title) - entries.append(tuple(entry_, False, False)) + entries.append(tuple[entry_, False, False]) return entries def db_get_files_episodes( self, - ) -> list[tuple(EpisodeFilesModel, bool, bool)] | None: + ) -> list[tuple[EpisodeFilesModel, bool, bool]] | None: entries = [] if not self.search_missing: return None @@ -1365,12 +1363,12 @@ def db_get_files_episodes( has_been_queried = True for i1, i2, i3 in self._search_todays(today_condition): if i1 is not None: - entries.append(tuple(i1, i2, i3)) + entries.append(tuple[i1, i2, i3]) return entries def db_get_files_movies( self, - ) -> list[tuple(MoviesFilesModel, bool, bool)] | None: + ) -> list[tuple[MoviesFilesModel, bool, bool]] | None: entries = [] if not self.search_missing: return None @@ -1401,7 +1399,7 @@ def db_get_files_movies( .order_by(self.model_file.Title.asc()) .execute() ): - entries.append(tuple(entry, False, False)) + entries.append(tuple[entry, False, False]) return entries def db_get_request_files(self) -> Iterable[MoviesFilesModel | EpisodeFilesModel]: From 933621822f3766bbe77834da316d3b81208277aa Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 17:03:26 +0100 Subject: [PATCH 06/10] Error fix 2 --- qBitrr/arss.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/qBitrr/arss.py b/qBitrr/arss.py index b685883e..4ef0d939 100755 --- a/qBitrr/arss.py +++ b/qBitrr/arss.py @@ -1149,16 +1149,13 @@ def db_get_files( ]: if self.type == "sonarr" and self.series_search: for series in self.db_get_files_series(): - for i1, i2, i3 in series: - yield i1, i2, i3, i3 is not True + yield series[0], series[1], series[2], i3 is not True elif self.type == "sonarr" and not self.series_search: for episodes in self.db_get_files_episodes(): - for i1, i2, i3 in episodes: - yield i1, i2, i3, False + yield series[0], series[1], series[2], False elif self.type == "radarr": for movies in self.db_get_files_movies(): - for i1, i2, i3 in movies: - yield i1, i2, i3, False + yield series[0], series[1], series[2], False def db_maybe_reset_entry_searched_state(self): if self.type == "sonarr": @@ -1268,7 +1265,7 @@ def db_reset__movie_searched_state(self): def db_get_files_series( self, - ) -> list[tuple[SeriesFilesModel, bool, bool]] | None: + ) -> list[list[SeriesFilesModel, bool, bool]] | None: entries = [] if not self.search_missing: return None @@ -1291,7 +1288,7 @@ def db_get_files_series( ) | self.model_file.SceneAbsoluteEpisodeNumber.is_null(False) for i1, i2, i3 in self._search_todays(condition): if i1 is not None: - entries.append(tuple[i1, i2, i3]) + entries.append([i1, i2, i3]) if not self.do_upgrade_search: condition = self.series_file_model.Searched == False else: @@ -1302,12 +1299,12 @@ def db_get_files_series( .order_by(self.series_file_model.EntryId.asc()) .execute() ): - entries.append(tuple[entry_, False, False]) + entries.append([entry_, False, False]) return entries def db_get_files_episodes( self, - ) -> list[tuple[EpisodeFilesModel, bool, bool]] | None: + ) -> list[list[EpisodeFilesModel, bool, bool]] | None: entries = [] if not self.search_missing: return None @@ -1363,12 +1360,12 @@ def db_get_files_episodes( has_been_queried = True for i1, i2, i3 in self._search_todays(today_condition): if i1 is not None: - entries.append(tuple[i1, i2, i3]) + entries.append([i1, i2, i3]) return entries def db_get_files_movies( self, - ) -> list[tuple[MoviesFilesModel, bool, bool]] | None: + ) -> list[list[MoviesFilesModel, bool, bool]] | None: entries = [] if not self.search_missing: return None @@ -1399,7 +1396,7 @@ def db_get_files_movies( .order_by(self.model_file.Title.asc()) .execute() ): - entries.append(tuple[entry, False, False]) + entries.append([entry, False, False]) return entries def db_get_request_files(self) -> Iterable[MoviesFilesModel | EpisodeFilesModel]: From 5db308291df4fb92681af765c9814d6cf2677ab4 Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 17:05:42 +0100 Subject: [PATCH 07/10] Error fix 3 --- .github/workflows/nightlyv4.yml | 2 +- qBitrr/arss.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightlyv4.yml b/.github/workflows/nightlyv4.yml index e5759113..3f4630a6 100644 --- a/.github/workflows/nightlyv4.yml +++ b/.github/workflows/nightlyv4.yml @@ -1,4 +1,4 @@ -name: Nightly +name: Nightly v4 on: push: diff --git a/qBitrr/arss.py b/qBitrr/arss.py index 4ef0d939..b327111c 100755 --- a/qBitrr/arss.py +++ b/qBitrr/arss.py @@ -1149,7 +1149,7 @@ def db_get_files( ]: if self.type == "sonarr" and self.series_search: for series in self.db_get_files_series(): - yield series[0], series[1], series[2], i3 is not True + yield series[0], series[1], series[2], series[2] is not True elif self.type == "sonarr" and not self.series_search: for episodes in self.db_get_files_episodes(): yield series[0], series[1], series[2], False From 45f2e71f0f4980881ba2c3c596d62154f6baa3a7 Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 17:40:48 +0100 Subject: [PATCH 08/10] Years search fix --- qBitrr/arss.py | 435 ++++++++----------------------------------------- 1 file changed, 67 insertions(+), 368 deletions(-) diff --git a/qBitrr/arss.py b/qBitrr/arss.py index b327111c..04dc0651 100755 --- a/qBitrr/arss.py +++ b/qBitrr/arss.py @@ -561,26 +561,6 @@ def is_downloading_state(torrent: TorrentDictionary) -> bool: TorrentStates.PAUSED_DOWNLOAD, ) - def _get_arr_modes( - self, - ) -> tuple[ - type[EpisodesModel] | type[MoviesModel] | type[MoviesModelv5], - type[CommandsModel], - type[SeriesModel] | type[SeriesModelv4] | type[MoviesMetadataModel], - ]: # sourcery skip: replace-interpolation-with-fstring, switch - if self.type == "sonarr": - if self.version.major == 3: - return EpisodesModel, CommandsModel, SeriesModel - elif self.version.major == 4: - return EpisodesModel, CommandsModel, SeriesModelv4 - elif self.type == "radarr": - if self.version.major == 4: - return MoviesModel, CommandsModel, MoviesMetadataModel - elif self.version.major == 5: - return MoviesModelv5, CommandsModel, MoviesMetadataModel - else: - raise UnhandledError("Well you shouldn't have reached here, Arr.type=%s" % self.type) - def _get_models( self, ) -> tuple[ @@ -1167,10 +1147,6 @@ def db_maybe_reset_entry_searched_state(self): def db_reset__series_searched_state(self): ids = [] - if self.version.major == 3: - self.model_arr_series_file: SeriesModel - elif self.version.major == 4: - self.model_arr_series_file: SeriesModelv4 self.series_file_model: SeriesFilesModel self.model_file: EpisodeFilesModel if ( @@ -1195,13 +1171,6 @@ def db_reset__series_searched_state(self): self.series_file_model.delete().where( self.series_file_model.EntryId.not_in(ids) ).execute() - # try: - # Ids = [id.Id for id in self.model_arr_series_file.select().execute()] - # self.series_file_model.delete().where( - # self.series_file_model.EntryId.not_in(Ids) - # ).execute() - # except peewee.DatabaseError: - # self.logger.error("Database error") def db_reset__episode_searched_state(self): ids = [] @@ -1228,11 +1197,6 @@ def db_reset__episode_searched_state(self): ) as e: completed = True self.model_file.delete().where(self.model_file.EntryId.not_in(ids)).execute() - # try: - # Ids = [id.Id for id in self.model_arr_file.select().execute()] - # self.model_file.delete().where(self.model_file.EntryId.not_in(Ids)).execute() - # except peewee.DatabaseError: - # self.logger.error("Database error") def db_reset__movie_searched_state(self): ids = [] @@ -1257,11 +1221,6 @@ def db_reset__movie_searched_state(self): ) as e: completed = True self.model_file.delete().where(self.model_file.EntryId.not_in(ids)).execute() - # try: - # Ids = [id.Id for id in self.model_arr_file.select().execute()] - # self.model_file.delete().where(self.model_file.EntryId.not_in(Ids)).execute() - # except peewee.DatabaseError: - # self.logger.error("Database error") def db_get_files_series( self, @@ -1457,15 +1416,10 @@ def db_request_update(self): self.db_ombi_update() def _db_request_update(self, request_ids: dict[str, set[int | str]]): - # with self.db.atomic(): try: if self.type == "sonarr" and any(i in request_ids for i in ["ImdbId", "TvdbId"]): - imdb_con = None - tvdb_con = None - if ImdbIds := request_ids.get("ImdbId"): - imdb_con = self.model_arr_series_file.ImdbId.in_(ImdbIds) - if tvDbIds := request_ids.get("TvdbId"): - tvdb_con = self.model_arr_series_file.TvdbId.in_(tvDbIds) + TvdbIds = request_ids.get("TvdbId") + ImdbIds = request_ids.get("ImdbId") completed = True while completed: try: @@ -1489,66 +1443,27 @@ def _db_request_update(self, request_ids: dict[str, set[int | str]]): continue if not self.search_specials and e["seasonNumber"] == 0: continue - if tvdb_con and imdb_con and "tvdbId" in e and "imdbId" in e: + if TvdbIds and TvdbIds and "tvdbId" in e and "imdbId" in e: if ( - series["tvdbId"] not in tvDbIds + series["tvdbId"] not in TvdbIds or series["imdbId"] not in ImdbIds ): continue - if imdb_con and "imdbId" in e: + if TvdbIds and "imdbId" in e: if series["imdbId"] not in ImdbIds: continue - if tvdb_con and "tvdbId" in e: - if series["tvdbId"] not in tvDbIds: + if TvdbIds and "tvdbId" in e: + if series["tvdbId"] not in TvdbIds: continue if not e["monitored"]: continue if e["episodeFileId"] != 0: continue self.db_update_single_series(db_entry=e, request=True) - # self.model_arr_file: EpisodesModel - # if self.version.major == 3: - # self.model_arr_series_file: SeriesModel - # elif self.version.major == 4: - # self.model_arr_series_file: SeriesModelv4 - # condition = self.model_arr_file.AirDateUtc.is_null(False) - # if not self.search_specials: - # condition &= self.model_arr_file.SeasonNumber != 0 - # condition &= self.model_arr_file.AbsoluteEpisodeNumber.is_null( - # False - # ) | self.model_arr_file.SceneAbsoluteEpisodeNumber.is_null(False) - # condition &= self.model_arr_file.AirDateUtc < datetime.now(timezone.utc) - # imdb_con = None - # tvdb_con = None - # if ImdbIds := request_ids.get("ImdbId"): - # imdb_con = self.model_arr_series_file.ImdbId.in_(ImdbIds) - # if tvDbIds := request_ids.get("TvdbId"): - # tvdb_con = self.model_arr_series_file.TvdbId.in_(tvDbIds) - # if imdb_con and tvdb_con: - # condition &= imdb_con | tvdb_con - # elif imdb_con: - # condition &= imdb_con - # elif tvdb_con: - # condition &= tvdb_con - # for db_entry in ( - # self.model_arr_file.select() - # .join( - # self.model_arr_series_file, - # on=(self.model_arr_file.SeriesId == self.model_arr_series_file.Id), - # join_type=JOIN.LEFT_OUTER, - # ) - # .switch(self.model_arr_file) - # .where(condition) - # .execute() - # ): - # self.db_update_single_series(db_entry=db_entry, request=True) + elif self.type == "radarr" and any(i in request_ids for i in ["ImdbId", "TmdbId"]): - tmdb_con = None - imdb_con = None - if ImdbIds := request_ids.get("ImdbId"): - imdb_con = self.model_arr_movies_file.ImdbId.in_(ImdbIds) - if TmdbIds := request_ids.get("TmdbId"): - tmdb_con = self.model_arr_movies_file.TmdbId.in_(TmdbIds) + ImdbIds = request_ids.get("ImdbId") + TmdbIds = request_ids.get("TmdbId") completed = True while completed: try: @@ -1563,13 +1478,13 @@ def _db_request_update(self, request_ids: dict[str, set[int | str]]): for m in movies: if m["year"] > datetime.now().year and m["year"] == 0: continue - if tmdb_con and imdb_con and "tmdbId" in m and "imdbId" in m: + if TmdbIds and ImdbIds and "tmdbId" in m and "imdbId" in m: if m["tmdbId"] not in TmdbIds or m["imdbId"] not in ImdbIds: continue - if imdb_con and "imdbId" in m: + if ImdbIds and "imdbId" in m: if m["imdbId"] not in ImdbIds: continue - if tmdb_con and "tmdbId" in m: + if TmdbIds and "tmdbId" in m: if m["tmdbId"] not in TmdbIds: continue if not m["monitored"]: @@ -1578,40 +1493,6 @@ def _db_request_update(self, request_ids: dict[str, set[int | str]]): continue self.db_update_single_series(db_entry=m, request=True) - # if self.version.major == 4: - # self.model_arr_file: MoviesModel - # elif self.version.major == 5: - # self.model_arr_file: MoviesModelv5 - # self.model_arr_movies_file: MoviesMetadataModel - # condition = self.model_arr_movies_file.Year <= datetime.now().year - # tmdb_con = None - # imdb_con = None - # if ImdbIds := request_ids.get("ImdbId"): - # imdb_con = self.model_arr_movies_file.ImdbId.in_(ImdbIds) - # if TmdbIds := request_ids.get("TmdbId"): - # tmdb_con = self.model_arr_movies_file.TmdbId.in_(TmdbIds) - # if tmdb_con and imdb_con: - # condition &= tmdb_con | imdb_con - # elif tmdb_con: - # condition &= tmdb_con - # elif imdb_con: - # condition &= imdb_con - # for db_entry in ( - # self.model_arr_file.select() - # .join( - # self.model_arr_movies_file, - # on=( - # self.model_arr_file.MovieMetadataId - # == self.model_arr_movies_file.Id - # ), - # join_type=JOIN.LEFT_OUTER, - # ) - # .switch(self.model_arr_file) - # .where(condition) - # .order_by(self.model_arr_file.Added.desc()) - # .execute() - # ): - # self.db_update_single_series(db_entry=db_entry, request=True) except requests.exceptions.ConnectionError: self.logger.error("Connection Error") raise DelayLoopException(length=300, type=self._name) @@ -1646,16 +1527,6 @@ def db_update_todays_releases(self): # with self.db.atomic(): if self.type == "sonarr": try: - # for series in self.model_arr_file.select().where( - # (self.model_arr_file.AirDateUtc.is_null(False)) - # & (self.model_arr_file.AirDateUtc < datetime.now(timezone.utc)) - # & (self.model_arr_file.AirDateUtc >= datetime.now(timezone.utc).date()) - # & ( - # self.model_arr_file.AbsoluteEpisodeNumber.is_null(False) - # | self.model_arr_file.SceneAbsoluteEpisodeNumber.is_null(False) - # ).execute() - # ): - # self.db_update_single_series(db_entry=series) completed = True while completed: try: @@ -1700,10 +1571,8 @@ def db_update(self): self.logger.trace(f"Started updating database") self.db_update_todays_releases() with self.db.atomic(): - # try: if self.type == "sonarr": if not self.series_search: - self.model_arr_file: EpisodesModel completed = True while completed: try: @@ -1768,42 +1637,7 @@ def db_update(self): if not e["monitored"]: continue self.db_update_single_series(db_entry=e) - # if self.search_by_year: - # series_query = self.model_arr_file.select().where( - # (self.model_arr_file.AirDateUtc.is_null(False)) - # & (self.model_arr_file.AirDateUtc < datetime.now(timezone.utc)) - # & ( - # self.model_arr_file.AbsoluteEpisodeNumber.is_null(False) - # | self.model_arr_file.SceneAbsoluteEpisodeNumber.is_null(False) - # ) - # & ( - # self.model_arr_file.AirDateUtc - # >= datetime(month=1, day=1, year=int(self.search_current_year)) - # ) - # & ( - # self.model_arr_file.AirDateUtc - # <= datetime( - # month=12, day=31, year=int(self.search_current_year) - # ) - # ) - # ) - # else: - # series_query = self.model_arr_file.select().where( - # (self.model_arr_file.AirDateUtc.is_null(False)) - # & (self.model_arr_file.AirDateUtc < datetime.now(timezone.utc)) - # & ( - # self.model_arr_file.AbsoluteEpisodeNumber.is_null(False) - # | self.model_arr_file.SceneAbsoluteEpisodeNumber.is_null(False) - # ) - # ) - # if series_query: - # for series in series_query: - # _series.add(series.SeriesId) - # self.db_update_single_series(db_entry=series) - # for series in self.model_arr_file.select().where( - # self.model_arr_file.SeriesId.in_(_series) - # ): - # self.db_update_single_series(db_entry=series) + else: completed = True while completed: @@ -1830,16 +1664,6 @@ def db_update(self): if not s["monitored"]: continue self.db_update_single_series(db_entry=s, series=True) - # if self.version.major == 3: - # self.model_arr_series_file: SeriesModel - # elif self.version.major == 4: - # self.model_arr_series_file: SeriesModelv4 - # for series in ( - # self.model_arr_series_file.select() - # .order_by(self.model_arr_series_file.Added.desc()) - # .execute() - # ): - # self.db_update_single_series(db_entry=series, series=True) elif self.type == "radarr": completed = True while completed: @@ -1866,50 +1690,11 @@ def db_update(self): if not m["monitored"]: continue self.db_update_single_series(db_entry=m) - # if self.version.major == 4: - # self.model_arr_file: MoviesModel - # elif self.version.major == 5: - # self.model_arr_file: MoviesModelv5 - # if self.search_by_year: - # for movies in ( - # self.model_arr_file.select(self.model_arr_file) - # .join( - # self.model_arr_movies_file, - # on=( - # self.model_arr_file.MovieMetadataId - # == self.model_arr_movies_file.Id - # ), - # ) - # .switch(self.model_arr_file) - # .where(self.model_arr_movies_file.Year == self.search_current_year) - # .order_by(self.model_arr_file.Added.desc()) - # .execute() - # ): - # self.db_update_single_series(db_entry=movies) - - # else: - # for movies in ( - # self.model_arr_file.select(self.model_arr_file) - # .join( - # self.model_arr_movies_file, - # on=( - # self.model_arr_file.MovieMetadataId - # == self.model_arr_movies_file.Id - # ), - # ) - # .switch(self.model_arr_file) - # .order_by(self.model_arr_file.Added.desc()) - # .execute() - # ): - # self.db_update_single_series(db_entry=movies) - # except peewee.DatabaseError: - # self.logger.error("Database error") self.logger.trace(f"Finished updating database") def minimum_availability_check( self, db_entry: JsonObject, - # metadata: MoviesMetadataModel = None, ) -> bool: if db_entry["year"] > datetime.now().year or db_entry["year"] == 0: self.logger.trace( @@ -2158,7 +1943,6 @@ def db_update_single_series( searched = False if self.type == "sonarr": if not series: - # db_entry: EpisodesModel self.model_file: EpisodeFilesModel completed = True @@ -2274,10 +2058,6 @@ def db_update_single_series( else: return else: - # if self.version.major == 3: - # db_entry: SeriesModel - # elif self.version.major == 4: - # db_entry: SeriesModelv4 self.series_file_model: SeriesFilesModel EntryId = db_entry["id"] if db_entry["monitored"] == True: @@ -2367,10 +2147,6 @@ def db_update_single_series( elif self.type == "radarr": self.model_file: MoviesFilesModel - # if self.version.major == 4: - # db_entry: MoviesModel - # elif self.version.major == 5: - # db_entry: MoviesModelv5 searched = False completed = True while completed: @@ -2391,11 +2167,6 @@ def db_update_single_series( self.model_queue.EntryId == db_entry["id"] ).execute() - # movieMetadata = self.model_arr_movies_file.get( - # self.model_arr_movies_file.Id == db_entry.MovieMetadataId - # ) - # movieMetadata: MoviesMetadataModel - if self.minimum_availability_check(db_entry) and db_entry["monitored"] == True: title = db_entry["title"] monitored = db_entry["onitored"] @@ -4001,42 +3772,6 @@ class Meta: self.model_file = Files self.model_queue = Queue self.persistent_queue = PersistingQueue - - db1, db2, db3 = self._get_arr_modes() - - class Files(db1): - class Meta: - database = self.arr_db - if self.type == "sonarr": - table_name = "Episodes" - elif self.type == "radarr": - table_name = "Movies" - - class Commands(db2): - class Meta: - database = self.arr_db - table_name = "Commands" - - if self.type == "sonarr": - - class Series(db3): - class Meta: - database = self.arr_db - table_name = "Series" - - self.model_arr_series_file = Series - - elif self.type == "radarr": - - class Movies(db3): - class Meta: - database = self.arr_db - table_name = "MovieMetadata" - - self.model_arr_movies_file = Movies - - self.model_arr_file = Files - self.model_arr_command = Commands self.search_setup_completed = True def run_request_search(self): @@ -4097,98 +3832,62 @@ def run_request_search(self): def get_year_search(self) -> tuple[list[int], int]: years_list = set() years = [] - with self.db.atomic(): - if self.type == "radarr": - if self.search_in_reverse: - years_query = ( - self.model_arr_movies_file.select( - self.model_arr_movies_file.Year.distinct() - ) - .where( - self.model_arr_movies_file.Year - <= datetime.now().year & self.model_arr_movies_file.Year - != 0 - ) - .order_by(self.model_arr_movies_file.Year.asc()) - .execute() - ) - else: - years_query = ( - self.model_arr_movies_file.select( - self.model_arr_movies_file.Year.distinct() - ) - .where( - self.model_arr_movies_file.Year - <= datetime.now().year & self.model_arr_movies_file.Year - != 0 - ) - .order_by(self.model_arr_movies_file.Year.desc()) - .execute() - ) - years = [y.Year for y in years_query] - self.logger.trace("Years: %s", years) - years_count = len(years) - elif self.type == "sonarr": - completed = True - while completed: - completed = False - try: - series = self.client.get_series() - except ( - requests.exceptions.ChunkedEncodingError, - requests.exceptions.ContentDecodingError, - requests.exceptions.ConnectionError, - ) as e: - completed = True + if self.type == "radarr": + completed = True + while completed: + completed = False + try: + movies = self.client.get_movie() + except ( + requests.exceptions.ChunkedEncodingError, + requests.exceptions.ContentDecodingError, + requests.exceptions.ConnectionError, + ) as e: + completed = True - for s in series: - episodes = self.client.get_episode(s["id"], True) - for e in episodes: - if "airDateUtc" in e: - if not self.search_specials and e["seasonNumber"] == 0: - continue - if not e["monitored"]: - continue - years_list.add( - datetime.strptime(e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ") - .replace(tzinfo=timezone.utc) - .year - ) + for m in movies: + if not m["monitored"]: + continue + if m["year"] != 0 and m["year"] <= datetime.now(timezone.utc).year: + years_list.add(m["year"]) - years_list = dict.fromkeys(years_list) - if self.search_in_reverse: - for key, file_dir in sorted( - list(years_list.items()), key=lambda x: x[0], reverse=True - ): - years.append(key) + elif self.type == "sonarr": + completed = True + while completed: + completed = False + try: + series = self.client.get_series() + except ( + requests.exceptions.ChunkedEncodingError, + requests.exceptions.ContentDecodingError, + requests.exceptions.ConnectionError, + ) as e: + completed = True - else: - for key, file_dir in sorted( - list(years_list.items()), key=lambda x: x[0], reverse=False - ): - years.append(key) - # self.model_arr_file: EpisodesModel - # if self.search_in_reverse: - # years_query = ( - # self.model_arr_file.select( - # fn.Substr(self.model_arr_file.AirDate, 1, 4).distinct().alias("Year") - # ) - # .where(fn.Substr(self.model_arr_file.AirDate, 1, 4) <= datetime.now()) - # .order_by(fn.Substr(self.model_arr_file.AirDate, 1, 4).asc()) - # .execute() - # ) - # else: - # years_query = ( - # self.model_arr_file.select( - # fn.Substr(self.model_arr_file.AirDate, 1, 4).distinct().alias("Year") - # ) - # .where(fn.Substr(self.model_arr_file.AirDate, 1, 4) <= datetime.now()) - # .order_by(fn.Substr(self.model_arr_file.AirDate, 1, 4).desc()) - # .execute() - # ) - # years = [y.Year for y in years_query] - self.logger.trace("Years: %s", years) - years_count = len(years) + for s in series: + episodes = self.client.get_episode(s["id"], True) + for e in episodes: + if "airDateUtc" in e: + if not self.search_specials and e["seasonNumber"] == 0: + continue + if not e["monitored"]: + continue + years_list.add( + datetime.strptime(e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ") + .replace(tzinfo=timezone.utc) + .year + ) + + years_list = dict.fromkeys(years_list) + if self.search_in_reverse: + for key, null in sorted(list(years_list.items()), key=lambda x: x[0], reverse=True): + years.append(key) + + else: + for key, null in sorted(list(years_list.items()), key=lambda x: x[0], reverse=False): + years.append(key) + self.logger.trace("Years: %s", years) + years_count = len(years) self.logger.trace("Years count: %s, Years: %s", years_count, years) return years, years_count From 2952f257e61b64a173ad3e328437dbed575bfd84 Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 17:49:34 +0100 Subject: [PATCH 09/10] Removed logic related to the arr DB files as they are no longer required --- config.example.toml | 388 ++++++++++++++++++++++--------------------- qBitrr/arr_tables.py | 178 -------------------- qBitrr/arss.py | 236 +++++++++++--------------- qBitrr/gen_config.py | 22 --- 4 files changed, 297 insertions(+), 527 deletions(-) delete mode 100644 qBitrr/arr_tables.py diff --git a/config.example.toml b/config.example.toml index 96da22f4..733441fb 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,5 +1,5 @@ # This is a config file for the qBitrr Script - Make sure to change all entries of "CHANGE_ME". -# This is a config file should be moved to "C:\Users\\qBitrr\qBitManager\config.toml". +# This is a config file should be moved to "/config". [Settings] @@ -34,11 +34,12 @@ PingURLS = ["one.one.one.one", "dns.google.com"] # FFprobe auto updates, binaries are downloaded from https://ffbinaries.com/downloads # If this is disabled and you want ffprobe to work -# Ensure that you add the binary for your platform into ~/qBitrr/qBitManager i.e "C:\Users\\qBitrr\qBitManager\ffprobe.exe" +# Ensure that you add the ffprobe binary to the folder"/config/qBitManager/ffprobe.exe" # If no `ffprobe` binary is found in the folder above all ffprobe functionality will be disabled. # By default this will always be on even if config does not have these key - to disable you need to explicitly set it to `False` FFprobeAutoUpdate = true + [qBit] # If this is enable qBitrr can run in a headless mode where it will only process searches. # If media search is enabled in their individual categories @@ -69,7 +70,7 @@ URI = "CHANGE_ME" APIKey = "CHANGE_ME" # Category applied by Servarr to torrents in qBitTorrent, can be found in Settings > Download Clients > qBit > Category -Category = "sonarr" +Category = "sonarr-tv" # Toggle whether to send a query to Servarr to search any failed torrents ReSearch = true @@ -86,7 +87,7 @@ RefreshDownloadsTimer = 1 # Error messages shown my the Arr instance which should be considered failures. # This entry should be a list, leave it empty if you want to disable this error handling. # If enabled qBitrr will remove the failed files and tell the Arr instance the download failed -ArrErrorCodesToBlocklist = [] +ArrErrorCodesToBlocklist = ["Not an upgrade for existing episode file(s)", "Not a preferred word upgrade for existing episode file(s)", "Unable to determine if file is a sample"] [Sonarr-TV.EntrySearch] @@ -100,12 +101,7 @@ AlsoSearchSpecials = false # Maximum allowed Searches at any one points (I wouldn't recommend settings this too high) # Sonarr has a hardcoded cap of 3 simultaneous tasks -SearchLimit = 3 - -# Servarr Datapath file path -# This is required for any of the search functionality to work -# The only exception for this is the "ReSearch" setting as that is done via an API call. -DatabaseFile = "CHANGE_ME/sonarr.db" +SearchLimit = 5 # It will order searches by the year the EPISODE was first aired SearchByYear = true @@ -125,13 +121,27 @@ QualityUnmetSearch = false # Once you have search all files on your specified year range restart the loop and search again. SearchAgainOnSearchCompletion = true -# Search by series instead of by episode +# Search by series instead of by episode (This ignored the QualityUnmetSearch setting) SearchBySeries = true # Prioritize Today's releases (Similar effect as RSS Sync, where it searches today's release episodes first, only works on Sonarr). PrioritizeTodaysReleases = true +[Sonarr-TV.EntrySearch.Ombi] +# Search Ombi for pending requests (Will only work if 'SearchMissing' is enabled.) +SearchOmbiRequests = false + +# Ombi URI (Note that this has to be the instance of Ombi which manage the Arr instance request (If you have multiple Ombi instances) +OmbiURI = "CHANGE_ME" + +# Ombi's API Key +OmbiAPIKey = "CHANGE_ME" + +# Only process approved requests +ApprovedOnly = true + + [Sonarr-TV.EntrySearch.Overseerr] # Search Overseerr for pending requests (Will only work if 'SearchMissing' is enabled.) # If this and Ombi are both enable, Ombi will be ignored @@ -141,12 +151,13 @@ SearchOverseerrRequests = false OverseerrURI = "CHANGE_ME" # Overseerr's API Key -OverseerrAPIKey = "CHANGE_ME=" +OverseerrAPIKey = "CHANGE_ME" # Only process approved requests ApprovedOnly = true -#Only for 4K Instances +# Only for 4K Instances +# Only for 4K Instances Is4K = false @@ -156,45 +167,22 @@ CaseSensitiveMatches = false # These regex values will match any folder where the full name matches the specified values here, comma separated strings. # These regex need to be escaped, that's why you see so many backslashes. -FolderExclusionRegex = [ - "\\bfeaturettes?\\b", - "\\bsamples?\\b", - "\\bscreens?\\b", - "\\bspecials?\\b", - "\\bova\\b", - "\\bnc(ed|op)?(\\\\d+)?\\b", -] +FolderExclusionRegex = ["\\bextras?\\b", "\\bfeaturettes?\\b", "\\bsamples?\\b", "\\bscreens?\\b", "\\bnc(ed|op)?(\\\\d+)?\\b"] # These regex values will match any folder where the full name matches the specified values here, comma separated strings. # These regex need to be escaped, that's why you see so many backslashes. -FileNameExclusionRegex = [ - "\\bncop\\\\d+?\\b", - "\\bnced\\\\d+?\\b", - "\\bsample\\b", - "brarbg.com\\b", - "\\btrailer\\b", - "music video", - "comandotorrents.com", -] +FileNameExclusionRegex = ["\\bncop\\\\d+?\\b", "\\bnced\\\\d+?\\b", "\\bsample\\b", "brarbg.com\\b", "\\btrailer\\b", "music video", "comandotorrents.com"] # Only files with these extensions will be allowed to be downloaded, comma separated strings or regex, leave it empty to allow all extensions -FileExtensionAllowlist = [ - ".mp4", - ".mkv", - ".sub", - ".ass", - ".srt", - ".!qB", - ".parts", - ".avi", -] +FileExtensionAllowlist = [".mp4", ".mkv", ".sub", ".ass", ".srt", ".!qB", ".parts"] # Auto delete files that can't be playable (i.e .exe, .png) -AutoDelete = true +AutoDelete = false # Ignore Torrents which are younger than this value (in seconds: 600 = 10 Minutes) IgnoreTorrentsYoungerThan = 180 +# Maximum allowed remaining ETA for torrent completion (in seconds: 3600 = 1 Hour) # Maximum allowed remaining ETA for torrent completion (in seconds: 3600 = 1 Hour) # Note that if you set the MaximumETA on a tracker basis that value is favoured over this value MaximumETA = 604800 @@ -205,10 +193,12 @@ MaximumDeletablePercentage = 0.99 # Ignore slow torrents. DoNotRemoveSlow = true +Trackers = [] + [Sonarr-TV.Torrent.SeedingMode] # Set the maximum allowed download rate for torrents # Set this value to -1 to disabled it -# Note that if you set the DownloadRateLimit on a tracker basis that value is avoured over this value +# Note that if you set the DownloadRateLimit on a tracker basis that value is favoured over this value DownloadRateLimitPerTorrent = -1 # Set the maximum allowed upload rate for torrents @@ -221,21 +211,19 @@ UploadRateLimitPerTorrent = -1 # Note that if you set the MaxUploadRatio on a tracker basis that value is favoured over this value MaxUploadRatio = -1 -# Set the maximum seeding time for torrents +# Set the maximum seeding time in seconds for torrents # Set this value to -1 to disabled it # Note that if you set the MaxSeedingTime on a tracker basis that value is favoured over this value MaxSeedingTime = -1 +# Remove torrent condition (-1=Do not remove, 1=Remove on MaxUploadRatio, 2=Remove on MaxSeedingTime, 3=Remove on MaxUploadRatio or MaxSeedingTime, 4=Remove on MaxUploadRatio and MaxSeedingTime) +RemoveTorrent = -1 + # Enable if you want to remove dead trackers RemoveDeadTrackers = false # If "RemoveDeadTrackers" is set to true then remove trackers with the following messages -RemoveTrackerWithMessage = [ - "skipping tracker announce (unreachable)", - "No such host is known", - "unsupported URL protocol", - "info hash is not authorized with this tracker", -] +RemoveTrackerWithMessage = ["skipping tracker announce (unreachable)", "No such host is known", "unsupported URL protocol", "info hash is not authorized with this tracker"] # You can have multiple trackers set here or none just add more subsections. @@ -250,7 +238,7 @@ URI = "CHANGE_ME" APIKey = "CHANGE_ME" # Category applied by Servarr to torrents in qBitTorrent, can be found in Settings > Download Clients > qBit > Category -Category = "sonarranime" +Category = "sonarr-anime" # Toggle whether to send a query to Servarr to search any failed torrents ReSearch = true @@ -267,7 +255,7 @@ RefreshDownloadsTimer = 1 # Error messages shown my the Arr instance which should be considered failures. # This entry should be a list, leave it empty if you want to disable this error handling. # If enabled qBitrr will remove the failed files and tell the Arr instance the download failed -ArrErrorCodesToBlocklist = [] +ArrErrorCodesToBlocklist = ["Not an upgrade for existing episode file(s)", "Not a preferred word upgrade for existing episode file(s)", "Unable to determine if file is a sample"] [Sonarr-Anime.EntrySearch] @@ -281,12 +269,7 @@ AlsoSearchSpecials = false # Maximum allowed Searches at any one points (I wouldn't recommend settings this too high) # Sonarr has a hardcoded cap of 3 simultaneous tasks -SearchLimit = 3 - -# Servarr Datapath file path -# This is required for any of the search functionality to work -# The only exception for this is the "ReSearch" setting as that is done via an API call. -DatabaseFile = "CHANGE_ME/sonarr.db" +SearchLimit = 5 # It will order searches by the year the EPISODE was first aired SearchByYear = true @@ -306,13 +289,27 @@ QualityUnmetSearch = false # Once you have search all files on your specified year range restart the loop and search again. SearchAgainOnSearchCompletion = true -# Search by series instead of by episode +# Search by series instead of by episode (This ignored the QualityUnmetSearch setting) SearchBySeries = true # Prioritize Today's releases (Similar effect as RSS Sync, where it searches today's release episodes first, only works on Sonarr). PrioritizeTodaysReleases = true +[Sonarr-Anime.EntrySearch.Ombi] +# Search Ombi for pending requests (Will only work if 'SearchMissing' is enabled.) +SearchOmbiRequests = false + +# Ombi URI (Note that this has to be the instance of Ombi which manage the Arr instance request (If you have multiple Ombi instances) +OmbiURI = "CHANGE_ME" + +# Ombi's API Key +OmbiAPIKey = "CHANGE_ME" + +# Only process approved requests +ApprovedOnly = true + + [Sonarr-Anime.EntrySearch.Overseerr] # Search Overseerr for pending requests (Will only work if 'SearchMissing' is enabled.) # If this and Ombi are both enable, Ombi will be ignored @@ -322,12 +319,13 @@ SearchOverseerrRequests = false OverseerrURI = "CHANGE_ME" # Overseerr's API Key -OverseerrAPIKey = "CHANGE_ME=" +OverseerrAPIKey = "CHANGE_ME" # Only process approved requests ApprovedOnly = true -#Only for 4K Instances +# Only for 4K Instances +# Only for 4K Instances Is4K = false @@ -337,43 +335,22 @@ CaseSensitiveMatches = false # These regex values will match any folder where the full name matches the specified values here, comma separated strings. # These regex need to be escaped, that's why you see so many backslashes. -FolderExclusionRegex = [ - "\\bfeaturettes?\\b", - "\\bsamples?\\b", - "\\bscreens?\\b", - "\\bnc(ed|op)?(\\\\d+)?\\b", -] +FolderExclusionRegex = ["\\bextras?\\b", "\\bfeaturettes?\\b", "\\bsamples?\\b", "\\bscreens?\\b", "\\bspecials?\\b", "\\bova\\b", "\\bnc(ed|op)?(\\\\d+)?\\b"] # These regex values will match any folder where the full name matches the specified values here, comma separated strings. # These regex need to be escaped, that's why you see so many backslashes. -FileNameExclusionRegex = [ - "\\bncop\\\\d+?\\b", - "\\bnced\\\\d+?\\b", - "\\bsample\\b", - "brarbg.com\\b", - "\\btrailer\\b", - "music video", - "comandotorrents.com", -] +FileNameExclusionRegex = ["\\bncop\\\\d+?\\b", "\\bnced\\\\d+?\\b", "\\bsample\\b", "brarbg.com\\b", "\\btrailer\\b", "music video", "comandotorrents.com"] # Only files with these extensions will be allowed to be downloaded, comma separated strings or regex, leave it empty to allow all extensions -FileExtensionAllowlist = [ - ".mp4", - ".mkv", - ".sub", - ".ass", - ".srt", - ".!qB", - ".parts", - ".avi", -] +FileExtensionAllowlist = [".mp4", ".mkv", ".sub", ".ass", ".srt", ".!qB", ".parts"] # Auto delete files that can't be playable (i.e .exe, .png) -AutoDelete = true +AutoDelete = false # Ignore Torrents which are younger than this value (in seconds: 600 = 10 Minutes) IgnoreTorrentsYoungerThan = 180 +# Maximum allowed remaining ETA for torrent completion (in seconds: 3600 = 1 Hour) # Maximum allowed remaining ETA for torrent completion (in seconds: 3600 = 1 Hour) # Note that if you set the MaximumETA on a tracker basis that value is favoured over this value MaximumETA = 604800 @@ -384,10 +361,11 @@ MaximumDeletablePercentage = 0.99 # Ignore slow torrents. DoNotRemoveSlow = true + [Sonarr-Anime.Torrent.SeedingMode] # Set the maximum allowed download rate for torrents # Set this value to -1 to disabled it -# Note that if you set the DownloadRateLimit on a tracker basis that value is avoured over this value +# Note that if you set the DownloadRateLimit on a tracker basis that value is favoured over this value DownloadRateLimitPerTorrent = -1 # Set the maximum allowed upload rate for torrents @@ -400,24 +378,36 @@ UploadRateLimitPerTorrent = -1 # Note that if you set the MaxUploadRatio on a tracker basis that value is favoured over this value MaxUploadRatio = -1 -# Set the maximum seeding time for torrents +# Set the maximum seeding time in seconds for torrents # Set this value to -1 to disabled it # Note that if you set the MaxSeedingTime on a tracker basis that value is favoured over this value MaxSeedingTime = -1 +# Remove torrent condition (-1=Do not remove, 1=Remove on MaxUploadRatio, 2=Remove on MaxSeedingTime, 3=Remove on MaxUploadRatio or MaxSeedingTime, 4=Remove on MaxUploadRatio and MaxSeedingTime) +RemoveTorrent = -1 + # Enable if you want to remove dead trackers RemoveDeadTrackers = false # If "RemoveDeadTrackers" is set to true then remove trackers with the following messages -RemoveTrackerWithMessage = [ - "skipping tracker announce (unreachable)", - "No such host is known", - "unsupported URL protocol", - "info hash is not authorized with this tracker", -] +RemoveTrackerWithMessage = ["skipping tracker announce (unreachable)", "No such host is known", "unsupported URL protocol", "info hash is not authorized with this tracker"] # You can have multiple trackers set here or none just add more subsections. +[[Sonarr-Anime.Torrent.Trackers]] +Name = "Nyaa" +Priority = 10 +URI = "http://nyaa.tracker.wf:7777/announce" +MaximumETA = 18000 +DownloadRateLimit = -1 +UploadRateLimit = -1 +MaxUploadRatio = -1 +MaxSeedingTime = -1 +AddTrackerIfMissing = false +RemoveIfExists = false +SuperSeedMode = false +AddTags = ["qBitrr-anime"] + [Radarr-1080] # Toggle whether to manage the Servarr instance torrents. Managed = true @@ -429,7 +419,7 @@ URI = "CHANGE_ME" APIKey = "CHANGE_ME" # Category applied by Servarr to torrents in qBitTorrent, can be found in Settings > Download Clients > qBit > Category -Category = "radarr" +Category = "radarr-1080" # Toggle whether to send a query to Servarr to search any failed torrents ReSearch = true @@ -446,7 +436,7 @@ RefreshDownloadsTimer = 1 # Error messages shown my the Arr instance which should be considered failures. # This entry should be a list, leave it empty if you want to disable this error handling. # If enabled qBitrr will remove the failed files and tell the Arr instance the download failed -ArrErrorCodesToBlocklist = [] +ArrErrorCodesToBlocklist = ["Not an upgrade for existing movie file(s)", "Not a preferred word upgrade for existing movie file(s)", "Unable to determine if file is a sample"] [Radarr-1080.EntrySearch] @@ -458,17 +448,11 @@ SearchMissing = true # Should search for specials episodes? (Season 00) AlsoSearchSpecials = false -# Maximum allowed Searches at any one points (I wouldn't recommend settings this too high) # Radarr has a default of 3 simultaneous tasks, which can be increased up to 10 tasks # If you set the environment variable of "THREAD_LIMIT" to a number between and including 2-10 # Radarr devs have stated that this is an unsupported feature so you will not get any support for doing so from them. # That being said I've been daily driving 10 simultaneous tasks for quite a while now with no issues. -SearchLimit = 10 - -# Servarr Datapath file path -# This is required for any of the search functionality to work -# The only exception for this is the "ReSearch" setting as that is done via an API call. -DatabaseFile = "CHANGE_ME/radarr.db" +SearchLimit = 5 # It will order searches by the year the EPISODE was first aired SearchByYear = true @@ -489,6 +473,20 @@ QualityUnmetSearch = false SearchAgainOnSearchCompletion = true +[Radarr-1080.EntrySearch.Ombi] +# Search Ombi for pending requests (Will only work if 'SearchMissing' is enabled.) +SearchOmbiRequests = false + +# Ombi URI (Note that this has to be the instance of Ombi which manage the Arr instance request (If you have multiple Ombi instances) +OmbiURI = "CHANGE_ME" + +# Ombi's API Key +OmbiAPIKey = "CHANGE_ME" + +# Only process approved requests +ApprovedOnly = true + + [Radarr-1080.EntrySearch.Overseerr] # Search Overseerr for pending requests (Will only work if 'SearchMissing' is enabled.) # If this and Ombi are both enable, Ombi will be ignored @@ -498,12 +496,13 @@ SearchOverseerrRequests = false OverseerrURI = "CHANGE_ME" # Overseerr's API Key -OverseerrAPIKey = "CHANGE_ME=" +OverseerrAPIKey = "CHANGE_ME" # Only process approved requests ApprovedOnly = true -#Only for 4K Instances +# Only for 4K Instances +# Only for 4K Instances Is4K = false @@ -513,45 +512,22 @@ CaseSensitiveMatches = false # These regex values will match any folder where the full name matches the specified values here, comma separated strings. # These regex need to be escaped, that's why you see so many backslashes. -FolderExclusionRegex = [ - "\\bfeaturettes?\\b", - "\\bsamples?\\b", - "\\bscreens?\\b", - "\\bspecials?\\b", - "\\bova\\b", - "\\bnc(ed|op)?(\\\\d+)?\\b", -] +FolderExclusionRegex = ["\\bextras?\\b", "\\bfeaturettes?\\b", "\\bsamples?\\b", "\\bscreens?\\b", "\\bnc(ed|op)?(\\\\d+)?\\b"] # These regex values will match any folder where the full name matches the specified values here, comma separated strings. # These regex need to be escaped, that's why you see so many backslashes. -FileNameExclusionRegex = [ - "\\bncop\\\\d+?\\b", - "\\bnced\\\\d+?\\b", - "\\bsample\\b", - "brarbg.com\\b", - "\\btrailer\\b", - "music video", - "comandotorrents.com", -] +FileNameExclusionRegex = ["\\bncop\\\\d+?\\b", "\\bnced\\\\d+?\\b", "\\bsample\\b", "brarbg.com\\b", "\\btrailer\\b", "music video", "comandotorrents.com"] # Only files with these extensions will be allowed to be downloaded, comma separated strings or regex, leave it empty to allow all extensions -FileExtensionAllowlist = [ - ".mp4", - ".mkv", - ".sub", - ".ass", - ".srt", - ".!qB", - ".parts", - ".avi", -] +FileExtensionAllowlist = [".mp4", ".mkv", ".sub", ".ass", ".srt", ".!qB", ".parts"] # Auto delete files that can't be playable (i.e .exe, .png) -AutoDelete = true +AutoDelete = false # Ignore Torrents which are younger than this value (in seconds: 600 = 10 Minutes) IgnoreTorrentsYoungerThan = 180 +# Maximum allowed remaining ETA for torrent completion (in seconds: 3600 = 1 Hour) # Maximum allowed remaining ETA for torrent completion (in seconds: 3600 = 1 Hour) # Note that if you set the MaximumETA on a tracker basis that value is favoured over this value MaximumETA = 604800 @@ -562,10 +538,11 @@ MaximumDeletablePercentage = 0.99 # Ignore slow torrents. DoNotRemoveSlow = true + [Radarr-1080.Torrent.SeedingMode] # Set the maximum allowed download rate for torrents # Set this value to -1 to disabled it -# Note that if you set the DownloadRateLimit on a tracker basis that value is avoured over this value +# Note that if you set the DownloadRateLimit on a tracker basis that value is favoured over this value DownloadRateLimitPerTorrent = -1 # Set the maximum allowed upload rate for torrents @@ -583,22 +560,44 @@ MaxUploadRatio = -1 # Note that if you set the MaxSeedingTime on a tracker basis that value is favoured over this value MaxSeedingTime = -1 -#Remove torrent condition (-1=Do not remove, 1=Remove on MaxUploadRatio, 2=Remove on MaxSeedingTime, 3=Remove on MaxUploadRatio or MaxSeedingTime, 4=Remove on MaxUploadRatio and MaxSeedingTime) +# Remove torrent condition (-1=Do not remove, 1=Remove on MaxUploadRatio, 2=Remove on MaxSeedingTime, 3=Remove on MaxUploadRatio or MaxSeedingTime, 4=Remove on MaxUploadRatio and MaxSeedingTime) RemoveTorrent = -1 # Enable if you want to remove dead trackers RemoveDeadTrackers = false # If "RemoveDeadTrackers" is set to true then remove trackers with the following messages -RemoveTrackerWithMessage = [ - "skipping tracker announce (unreachable)", - "No such host is known", - "unsupported URL protocol", - "info hash is not authorized with this tracker", -] +RemoveTrackerWithMessage = ["skipping tracker announce (unreachable)", "No such host is known", "unsupported URL protocol", "info hash is not authorized with this tracker"] # You can have multiple trackers set here or none just add more subsections. +[[Radarr-1080.Torrent.Trackers]] +Name = "Rarbg-2810" +Priority = 1 +URI = "udp://9.rarbg.com:2810/announce" +MaximumETA = 18000 +DownloadRateLimit = -1 +UploadRateLimit = -1 +MaxUploadRatio = -1 +MaxSeedingTime = -1 +AddTrackerIfMissing = false +RemoveIfExists = false +SuperSeedMode = false +AddTags = ["qBitrr-Rarbg", "Movies and TV"] + +[[Radarr-1080.Torrent.Trackers]] +Name = "Rarbg-2740" +Priority = 2 +URI = "udp://9.rarbg.to:2740/announce" +MaximumETA = 18000 +DownloadRateLimit = -1 +UploadRateLimit = -1 +MaxUploadRatio = -1 +MaxSeedingTime = -1 +AddTrackerIfMissing = false +RemoveIfExists = false +SuperSeedMode = false + [Radarr-4K] # Toggle whether to manage the Servarr instance torrents. Managed = true @@ -610,7 +609,7 @@ URI = "CHANGE_ME" APIKey = "CHANGE_ME" # Category applied by Servarr to torrents in qBitTorrent, can be found in Settings > Download Clients > qBit > Category -Category = "radarr4k" +Category = "radarr-4k" # Toggle whether to send a query to Servarr to search any failed torrents ReSearch = true @@ -627,7 +626,7 @@ RefreshDownloadsTimer = 1 # Error messages shown my the Arr instance which should be considered failures. # This entry should be a list, leave it empty if you want to disable this error handling. # If enabled qBitrr will remove the failed files and tell the Arr instance the download failed -ArrErrorCodesToBlocklist = [] +ArrErrorCodesToBlocklist = ["Not an upgrade for existing movie file(s)", "Not a preferred word upgrade for existing movie file(s)", "Unable to determine if file is a sample"] [Radarr-4K.EntrySearch] @@ -639,17 +638,11 @@ SearchMissing = true # Should search for specials episodes? (Season 00) AlsoSearchSpecials = false -# Maximum allowed Searches at any one points (I wouldn't recommend settings this too high) # Radarr has a default of 3 simultaneous tasks, which can be increased up to 10 tasks # If you set the environment variable of "THREAD_LIMIT" to a number between and including 2-10 # Radarr devs have stated that this is an unsupported feature so you will not get any support for doing so from them. # That being said I've been daily driving 10 simultaneous tasks for quite a while now with no issues. -SearchLimit = 10 - -# Servarr Datapath file path -# This is required for any of the search functionality to work -# The only exception for this is the "ReSearch" setting as that is done via an API call. -DatabaseFile = "CHANGE_ME/radarr.db" +SearchLimit = 5 # It will order searches by the year the EPISODE was first aired SearchByYear = true @@ -670,6 +663,20 @@ QualityUnmetSearch = false SearchAgainOnSearchCompletion = true +[Radarr-4K.EntrySearch.Ombi] +# Search Ombi for pending requests (Will only work if 'SearchMissing' is enabled.) +SearchOmbiRequests = false + +# Ombi URI (Note that this has to be the instance of Ombi which manage the Arr instance request (If you have multiple Ombi instances) +OmbiURI = "CHANGE_ME" + +# Ombi's API Key +OmbiAPIKey = "CHANGE_ME" + +# Only process approved requests +ApprovedOnly = true + + [Radarr-4K.EntrySearch.Overseerr] # Search Overseerr for pending requests (Will only work if 'SearchMissing' is enabled.) # If this and Ombi are both enable, Ombi will be ignored @@ -679,12 +686,13 @@ SearchOverseerrRequests = false OverseerrURI = "CHANGE_ME" # Overseerr's API Key -OverseerrAPIKey = "CHANGE_ME=" +OverseerrAPIKey = "CHANGE_ME" # Only process approved requests ApprovedOnly = true -#Only for 4K Instances +# Only for 4K Instances +# Only for 4K Instances Is4K = true @@ -694,45 +702,22 @@ CaseSensitiveMatches = false # These regex values will match any folder where the full name matches the specified values here, comma separated strings. # These regex need to be escaped, that's why you see so many backslashes. -FolderExclusionRegex = [ - "\\bfeaturettes?\\b", - "\\bsamples?\\b", - "\\bscreens?\\b", - "\\bspecials?\\b", - "\\bova\\b", - "\\bnc(ed|op)?(\\\\d+)?\\b", -] +FolderExclusionRegex = ["\\bextras?\\b", "\\bfeaturettes?\\b", "\\bsamples?\\b", "\\bscreens?\\b", "\\bnc(ed|op)?(\\\\d+)?\\b"] # These regex values will match any folder where the full name matches the specified values here, comma separated strings. # These regex need to be escaped, that's why you see so many backslashes. -FileNameExclusionRegex = [ - "\\bncop\\\\d+?\\b", - "\\bnced\\\\d+?\\b", - "\\bsample\\b", - "brarbg.com\\b", - "\\btrailer\\b", - "music video", - "comandotorrents.com", -] +FileNameExclusionRegex = ["\\bncop\\\\d+?\\b", "\\bnced\\\\d+?\\b", "\\bsample\\b", "brarbg.com\\b", "\\btrailer\\b", "music video", "comandotorrents.com"] # Only files with these extensions will be allowed to be downloaded, comma separated strings or regex, leave it empty to allow all extensions -FileExtensionAllowlist = [ - ".mp4", - ".mkv", - ".sub", - ".ass", - ".srt", - ".!qB", - ".parts", - ".avi", -] +FileExtensionAllowlist = [".mp4", ".mkv", ".sub", ".ass", ".srt", ".!qB", ".parts"] # Auto delete files that can't be playable (i.e .exe, .png) -AutoDelete = true +AutoDelete = false # Ignore Torrents which are younger than this value (in seconds: 600 = 10 Minutes) IgnoreTorrentsYoungerThan = 180 +# Maximum allowed remaining ETA for torrent completion (in seconds: 3600 = 1 Hour) # Maximum allowed remaining ETA for torrent completion (in seconds: 3600 = 1 Hour) # Note that if you set the MaximumETA on a tracker basis that value is favoured over this value MaximumETA = 604800 @@ -743,10 +728,11 @@ MaximumDeletablePercentage = 0.99 # Ignore slow torrents. DoNotRemoveSlow = true + [Radarr-4K.Torrent.SeedingMode] # Set the maximum allowed download rate for torrents # Set this value to -1 to disabled it -# Note that if you set the DownloadRateLimit on a tracker basis that value is avoured over this value +# Note that if you set the DownloadRateLimit on a tracker basis that value is favoured over this value DownloadRateLimitPerTorrent = -1 # Set the maximum allowed upload rate for torrents @@ -759,20 +745,46 @@ UploadRateLimitPerTorrent = -1 # Note that if you set the MaxUploadRatio on a tracker basis that value is favoured over this value MaxUploadRatio = -1 -# Set the maximum seeding time for torrents +# Set the maximum seeding time in seconds for torrents # Set this value to -1 to disabled it # Note that if you set the MaxSeedingTime on a tracker basis that value is favoured over this value MaxSeedingTime = -1 +# Remove torrent condition (-1=Do not remove, 1=Remove on MaxUploadRatio, 2=Remove on MaxSeedingTime, 3=Remove on MaxUploadRatio or MaxSeedingTime, 4=Remove on MaxUploadRatio and MaxSeedingTime) +RemoveTorrent = -1 + # Enable if you want to remove dead trackers RemoveDeadTrackers = false # If "RemoveDeadTrackers" is set to true then remove trackers with the following messages -RemoveTrackerWithMessage = [ - "skipping tracker announce (unreachable)", - "No such host is known", - "unsupported URL protocol", - "info hash is not authorized with this tracker", -] +RemoveTrackerWithMessage = ["skipping tracker announce (unreachable)", "No such host is known", "unsupported URL protocol", "info hash is not authorized with this tracker"] # You can have multiple trackers set here or none just add more subsections. + +[[Radarr-4K.Torrent.Trackers]] +Name = "Rarbg-2810" +Priority = 1 +URI = "udp://9.rarbg.com:2810/announce" +MaximumETA = 18000 +DownloadRateLimit = -1 +UploadRateLimit = -1 +MaxUploadRatio = -1 +MaxSeedingTime = -1 +AddTrackerIfMissing = false +RemoveIfExists = false +SuperSeedMode = false +AddTags = ["qBitrr-Rarbg", "Movies and TV", "4K"] + +[[Radarr-4K.Torrent.Trackers]] +Name = "Rarbg-2740" +Priority = 2 +URI = "udp://9.rarbg.to:2740/announce" +MaximumETA = 18000 +DownloadRateLimit = -1 +UploadRateLimit = -1 +MaxUploadRatio = -1 +MaxSeedingTime = -1 +AddTrackerIfMissing = false +RemoveIfExists = false +SuperSeedMode = false +AddTags = ["4K"] diff --git a/qBitrr/arr_tables.py b/qBitrr/arr_tables.py deleted file mode 100644 index a40f89c8..00000000 --- a/qBitrr/arr_tables.py +++ /dev/null @@ -1,178 +0,0 @@ -from peewee import BooleanField, DateTimeField, IntegerField, Model, TextField - - -class CommandsModel(Model): - Id = IntegerField(null=False, primary_key=True) - Name = TextField() - Body = TextField() - Priority = IntegerField() - Status = IntegerField() - QueuedAt = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - StartedAt = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - EndedAt = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Duration = TextField() - Exception = TextField() - Trigger = IntegerField() - Result = IntegerField() - - -class MoviesMetadataModel(Model): - Id = IntegerField(null=False, primary_key=True) - TmdbId = IntegerField() - ImdbId = TextField() - Images = TextField() - Genres = TextField() - Title = TextField() - SortTitle = TextField() - CleanTitle = TextField() - OriginalTitle = TextField() - CleanOriginalTitle = TextField() - OriginalLanguage = IntegerField() - Status = IntegerField() - LastInfoSync = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Runtime = IntegerField() - InCinemas = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - PhysicalRelease = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - DigitalRelease = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Year = IntegerField() - SecondaryYear = IntegerField() - Ratings = TextField() - Recommendations = TextField() - Certification = TextField() - YouTubeTrailerId = TextField() - Studio = TextField() - Overview = TextField() - Website = TextField() - Popularity = IntegerField() - CollectionTmdbId = IntegerField() - CollectionTitle = TextField() - - -class MoviesModel(Model): - Id = IntegerField(null=False, primary_key=True) - Path = TextField() - Monitored = BooleanField() - ProfileId = IntegerField() - Added = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Tags = TextField() - AddOptions = TextField() - MovieFileId = IntegerField() - MinimumAvailability = IntegerField() - MovieMetadataId = IntegerField() - - -class MoviesModelv5(Model): - Id = IntegerField(null=False, primary_key=True) - Path = TextField() - Monitored = BooleanField() - QualityProfileId = IntegerField() - Added = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Tags = TextField() - AddOptions = TextField() - MovieFileId = IntegerField() - MinimumAvailability = IntegerField() - MovieMetadataId = IntegerField() - - -class EpisodesModel(Model): - Id = IntegerField(null=False, primary_key=True) - SeriesId = IntegerField(null=False) - SeasonNumber = IntegerField(null=False) - EpisodeNumber = IntegerField(null=False) - Title = TextField() - Overview = TextField() - EpisodeFileId = IntegerField() - AbsoluteEpisodeNumber = IntegerField() - SceneAbsoluteEpisodeNumber = IntegerField() - SceneSeasonNumber = IntegerField() - SceneEpisodeNumber = IntegerField() - Monitored = BooleanField() - AirDateUtc = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - AirDate = TextField() - Ratings = TextField() - Images = TextField() - UnverifiedSceneNumbering = BooleanField(null=False, default=False) - LastSearchTime = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - AiredAfterSeasonNumber = IntegerField() - AiredBeforeSeasonNumber = IntegerField() - AiredBeforeEpisodeNumber = IntegerField() - TvdbId = IntegerField() - Runtime = IntegerField() - FinaleType = TextField() - - -class SeriesModel(Model): - Id = IntegerField(null=False, primary_key=True) - TvdbId = IntegerField() - TvRageId = IntegerField() - ImdbId = TextField() - Title = TextField() - TitleSlug = TextField() - CleanTitle = TextField() - Status = IntegerField() - Overview = TextField() - AirTime = TextField() - Images = TextField() - Path = TextField() - Monitored = BooleanField() - SeasonFolder = IntegerField() - LastInfoSync = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - LastDiskSync = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Runtime = IntegerField() - SeriesType = IntegerField() - Network = TextField() - UseSceneNumbering = BooleanField() - FirstAired = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - NextAiring = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Year = IntegerField() - Seasons = TextField() - Actors = TextField() - Ratings = TextField() - Genres = TextField() - Certification = TextField() - SortTitle = TextField() - QualityProfileId = IntegerField() - Tags = TextField() - Added = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - AddOptions = TextField() - TvMazeId = IntegerField() - OriginalLanguage = IntegerField() - - -class SeriesModelv4(Model): - Id = IntegerField(null=False, primary_key=True) - TvdbId = IntegerField() - TvRageId = IntegerField() - ImdbId = TextField() - Title = TextField() - TitleSlug = TextField() - CleanTitle = TextField() - Status = IntegerField() - Overview = TextField() - AirTime = TextField() - Images = TextField() - Path = TextField() - Monitored = BooleanField() - SeasonFolder = IntegerField() - LastInfoSync = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - LastDiskSync = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Runtime = IntegerField() - SeriesType = IntegerField() - Network = TextField() - UseSceneNumbering = BooleanField() - FirstAired = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - NextAiring = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - Year = IntegerField() - Seasons = TextField() - Actors = TextField() - Ratings = TextField() - Genres = TextField() - Certification = TextField() - SortTitle = TextField() - QualityProfileId = IntegerField() - Tags = TextField() - Added = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) - AddOptions = TextField() - TvMazeId = IntegerField() - OriginalLanguage = IntegerField() - LastAired = DateTimeField(formats=["%Y-%m-%d %H:%M:%S.%f"]) diff --git a/qBitrr/arss.py b/qBitrr/arss.py index 04dc0651..6bae3fa2 100755 --- a/qBitrr/arss.py +++ b/qBitrr/arss.py @@ -26,15 +26,6 @@ from qbittorrentapi import TorrentDictionary, TorrentStates from ujson import JSONDecodeError -from qBitrr.arr_tables import ( - CommandsModel, - EpisodesModel, - MoviesMetadataModel, - MoviesModel, - MoviesModelv5, - SeriesModel, - SeriesModelv4, -) from qBitrr.config import ( APPDATA_FOLDER, COMPLETED_DOWNLOAD_FOLDER, @@ -227,21 +218,8 @@ def __init__( self._delta = 1 else: self._delta = -1 - arr_db_file = CONFIG.get(f"{name}.EntrySearch.DatabaseFile", fallback=None) - self.arr_db_file = pathlib.Path("/.Invalid Place Holder") - if self.search_missing and arr_db_file is None: - self.logger.critical("Arr DB file not specified setting SearchMissing to False") - self.search_missing = False - if arr_db_file is not None: - self.arr_db_file = pathlib.Path(arr_db_file) self._app_data_folder = APPDATA_FOLDER self.search_db_file = self._app_data_folder.joinpath(f"{self._name}.db") - if self.search_missing and not self.arr_db_file.exists(): - self.logger.critical( - "Arr DB file cannot be located setting SearchMissing to False: %s", - self.arr_db_file, - ) - self.search_missing = False self.ombi_search_requests = CONFIG.get( f"{name}.EntrySearch.Ombi.SearchOmbiRequests", fallback=False @@ -423,10 +401,6 @@ def __init__( "Script Config: CommandLimit=%s", self.search_command_limit, ) - self.logger.debug( - "Script Config: DatabaseFile=%s", - self.arr_db_file, - ) self.logger.debug( "Script Config: MaximumDeletablePercentage=%s", self.maximum_deletable_percentage, @@ -489,11 +463,6 @@ def __init__( ["qBitrr-allowed_seeding", "qBitrr-ignored"] ) self.search_setup_completed = False - self.model_arr_file: EpisodesModel | MoviesModel | MoviesModelv5 = None - self.model_arr_series_file: SeriesModel | SeriesModelv4 = None - self.model_arr_movies_file: MoviesMetadataModel = None - - self.model_arr_command: CommandsModel = None self.model_file: EpisodeFilesModel | MoviesFilesModel = None self.series_file_model: SeriesFilesModel = None self.model_queue: EpisodeQueueModel | MovieQueueModel = None @@ -1524,7 +1493,6 @@ def db_ombi_update(self): def db_update_todays_releases(self): if not self.prioritize_todays_release: return - # with self.db.atomic(): if self.type == "sonarr": try: completed = True @@ -1570,106 +1538,77 @@ def db_update(self): return self.logger.trace(f"Started updating database") self.db_update_todays_releases() - with self.db.atomic(): - if self.type == "sonarr": - if not self.series_search: - completed = True - while completed: - try: - completed = False - series = self.client.get_series() - except ( - requests.exceptions.ChunkedEncodingError, - requests.exceptions.ContentDecodingError, - requests.exceptions.ConnectionError, - ): - completed = True - if self.search_by_year: - for s in series: - episodes = self.client.get_episode(s["id"], True) - for e in episodes: - if "airDateUtc" in e and ( - "absoluteEpisodeNumber" in e - or "sceneAbsoluteEpisodeNumber" in e + if self.type == "sonarr": + if not self.series_search: + completed = True + while completed: + try: + completed = False + series = self.client.get_series() + except ( + requests.exceptions.ChunkedEncodingError, + requests.exceptions.ContentDecodingError, + requests.exceptions.ConnectionError, + ): + completed = True + if self.search_by_year: + for s in series: + episodes = self.client.get_episode(s["id"], True) + for e in episodes: + if "airDateUtc" in e and ( + "absoluteEpisodeNumber" in e or "sceneAbsoluteEpisodeNumber" in e + ): + if datetime.strptime( + e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ" + ).replace(tzinfo=timezone.utc) > datetime.now(timezone.utc): + continue + if ( + datetime.strptime(e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ") + .replace(tzinfo=timezone.utc) + .date() + < datetime( + month=1, day=1, year=int(self.search_current_year) + ).date() ): - if datetime.strptime( - e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ" - ).replace(tzinfo=timezone.utc) > datetime.now(timezone.utc): - continue - if ( - datetime.strptime(e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ") - .replace(tzinfo=timezone.utc) - .date() - < datetime( - month=1, day=1, year=int(self.search_current_year) - ).date() - ): - continue - if ( - datetime.strptime(e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ") - .replace(tzinfo=timezone.utc) - .date() - > datetime( - month=12, day=31, year=int(self.search_current_year) - ).date() - ): - continue - if not self.search_specials and e["seasonNumber"] == 0: - continue - if not e["monitored"]: - continue - self.db_update_single_series(db_entry=e) - - else: - for s in series: - episodes = self.client.get_episode(s["id"], True) - for e in episodes: - if "airDateUtc" in e and ( - "absoluteEpisodeNumber" in e - or "sceneAbsoluteEpisodeNumber" in e + continue + if ( + datetime.strptime(e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ") + .replace(tzinfo=timezone.utc) + .date() + > datetime( + month=12, day=31, year=int(self.search_current_year) + ).date() ): - if datetime.strptime( - e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ" - ).replace(tzinfo=timezone.utc) > datetime.now(timezone.utc): - continue - if not self.search_specials and e["seasonNumber"] == 0: - continue - if not e["monitored"]: - continue - self.db_update_single_series(db_entry=e) + continue + if not self.search_specials and e["seasonNumber"] == 0: + continue + if not e["monitored"]: + continue + self.db_update_single_series(db_entry=e) else: - completed = True - while completed: - try: - completed = False - series = self.client.get_series() - except ( - requests.exceptions.ChunkedEncodingError, - requests.exceptions.ContentDecodingError, - requests.exceptions.ConnectionError, - ): - completed = True - if self.search_by_year: - for s in series: - if s["year"] < self.search_current_year: - continue - if s["year"] > self.search_current_year: - continue - if not s["monitored"]: - continue - self.db_update_single_series(db_entry=s, series=True) - else: - for s in series: - if not s["monitored"]: - continue - self.db_update_single_series(db_entry=s, series=True) - elif self.type == "radarr": + for s in series: + episodes = self.client.get_episode(s["id"], True) + for e in episodes: + if "airDateUtc" in e and ( + "absoluteEpisodeNumber" in e or "sceneAbsoluteEpisodeNumber" in e + ): + if datetime.strptime( + e["airDateUtc"], "%Y-%m-%dT%H:%M:%SZ" + ).replace(tzinfo=timezone.utc) > datetime.now(timezone.utc): + continue + if not self.search_specials and e["seasonNumber"] == 0: + continue + if not e["monitored"]: + continue + self.db_update_single_series(db_entry=e) + + else: completed = True while completed: try: completed = False - movies = self.client.get_movie() + series = self.client.get_series() except ( requests.exceptions.ChunkedEncodingError, requests.exceptions.ContentDecodingError, @@ -1677,19 +1616,45 @@ def db_update(self): ): completed = True if self.search_by_year: - for m in movies: - if m["year"] < self.search_current_year: + for s in series: + if s["year"] < self.search_current_year: continue - if m["year"] > self.search_current_year: + if s["year"] > self.search_current_year: continue - if not m["monitored"]: + if not s["monitored"]: continue - self.db_update_single_series(db_entry=min) + self.db_update_single_series(db_entry=s, series=True) else: - for m in series: - if not m["monitored"]: + for s in series: + if not s["monitored"]: continue - self.db_update_single_series(db_entry=m) + self.db_update_single_series(db_entry=s, series=True) + elif self.type == "radarr": + completed = True + while completed: + try: + completed = False + movies = self.client.get_movie() + except ( + requests.exceptions.ChunkedEncodingError, + requests.exceptions.ContentDecodingError, + requests.exceptions.ConnectionError, + ): + completed = True + if self.search_by_year: + for m in movies: + if m["year"] < self.search_current_year: + continue + if m["year"] > self.search_current_year: + continue + if not m["monitored"]: + continue + self.db_update_single_series(db_entry=min) + else: + for m in series: + if not m["monitored"]: + continue + self.db_update_single_series(db_entry=m) self.logger.trace(f"Finished updating database") def minimum_availability_check( @@ -3723,13 +3688,6 @@ def register_search_mode(self): if self.search_missing is False: self.search_setup_completed = True return - if not self.arr_db_file.exists(): - self.search_missing = False - return - else: - self.arr_db = SqliteDatabase(None) - self.arr_db.init(f"file:{self.arr_db_file}?mode=ro", uri=True) - self.arr_db.connect() self.db = SqliteDatabase(None) self.db.init( diff --git a/qBitrr/gen_config.py b/qBitrr/gen_config.py index 81ff4bcc..41e57570 100755 --- a/qBitrr/gen_config.py +++ b/qBitrr/gen_config.py @@ -549,28 +549,6 @@ def _gen_default_search_table(category: str, cat_default: Table): "SearchLimit", 5, ) - if "sonarr" in category.lower(): - _gen_default_line( - search_table, - [ - "Servarr Datapath file path", - "This is required for any of the search functionality to work", - 'The only exception for this is the "ReSearch" setting as that is done via an API call.', - ], - "DatabaseFile", - "CHANGE_ME/sonarr.db", - ) - elif "radarr" in category.lower(): - _gen_default_line( - search_table, - [ - "Servarr Datapath file path", - "This is required for any of the search functionality to work", - 'The only exception for this is the "ReSearch" setting as that is done via an API call.', - ], - "DatabaseFile", - "CHANGE_ME/radarr.db", - ) _gen_default_line( search_table, "It will order searches by the year the EPISODE was first aired", From 9e808f781ae1d4ef73c5dda754abcc2f4397bbeb Mon Sep 17 00:00:00 2001 From: Feramance Date: Mon, 8 Jan 2024 17:53:17 +0100 Subject: [PATCH 10/10] Update README and PyPi description --- README.md | 11 ++--------- qBitrr2.egg-info/PKG-INFO | 11 ++--------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 723985e4..5efa708b 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ A simple script to monitor [qBit](https://github.com/qbittorrent/qBittorrent) an - **Sonarr v4 support** - **Radarr v4 and v5 support** - Available if provided with a Sonarr/Radarr database file: - - Monitor Arr's databases to trigger missing episode searches. + - Monitor Arr's to trigger missing episode searches. - Searches Radarr missing movies based on Minimum Availability - Customizable searching by series or singular episodes - Optionally searches year by year is ascending or descending order (config option available) @@ -99,8 +99,6 @@ docker run -d \ -e RESTART_TIMER=0 \ -v /etc/localtime:/etc/localtime:ro \ -v /path/to/appdata/qbitrr:/config \ - -v /path/to/sonarr/db:/databases/sonarr.db:ro \ - -v /path/to/radarr/db:/databases/radarr.db:ro \ -v /path/to/completed/downloads/folder:/completed_downloads:rw \ --restart unless-stopped \ feramance/qbitrr:latest @@ -123,13 +121,8 @@ services: volumes: - /etc/localtime:/etc/localtime:ro - /path/to/appdata/qbitrr:/config # Config folder for qBitrr - - /path/to/sonarr/db:/sonarr.db:ro # This is only needed if you want episode search handling :ro means it is only ever mounted as a read-only folder, the script never needs more than read access - - /path/to/radarr/db:/radarr.db:ro # This is only needed if you want movie search handling, :ro means it is only ever mounted as a read-only folder, the script never needs more than read access - /path/to/completed/downloads/folder:/completed_downloads:rw # The script will ALWAYS require write permission in this folder if mounted, this folder is used to monitor completed downloads and if not present will cause the script to ignore downloaded file monitoring. - # Now just to make sure it is clean, when using this script in a docker you will need to ensure you config.toml values reflect the mounted folders.# - # For example, for your Sonarr.DatabaseFile value using the values above you'd add - # DatabaseFile = /sonarr.db/path/in/container/sonarr.db - # Because this is where you mounted it to + # Now just to make sure it is clean, when using this script in a docker you will need to ensure you config.toml values reflect the mounted folders. # The same would apply to Settings.CompletedDownloadFolder # e.g CompletedDownloadFolder = /completed_downloads/folder/in/container diff --git a/qBitrr2.egg-info/PKG-INFO b/qBitrr2.egg-info/PKG-INFO index f4ade32e..d7032dbb 100644 --- a/qBitrr2.egg-info/PKG-INFO +++ b/qBitrr2.egg-info/PKG-INFO @@ -106,7 +106,7 @@ A simple script to monitor [qBit](https://github.com/qbittorrent/qBittorrent) an - **Sonarr v4 support** - **Radarr v4 and v5 support** - Available if provided with a Sonarr/Radarr database file: - - Monitor Arr's databases to trigger missing episode searches. + - Monitor Arr's to trigger missing episode searches. - Searches Radarr missing movies based on Minimum Availability - Customizable searching by series or singular episodes - Optionally searches year by year is ascending or descending order (config option available) @@ -169,8 +169,6 @@ docker run -d \ -e RESTART_TIMER=0 \ -v /etc/localtime:/etc/localtime:ro \ -v /path/to/appdata/qbitrr:/config \ - -v /path/to/sonarr/db:/databases/sonarr.db:ro \ - -v /path/to/radarr/db:/databases/radarr.db:ro \ -v /path/to/completed/downloads/folder:/completed_downloads:rw \ --restart unless-stopped \ feramance/qbitrr:latest @@ -193,13 +191,8 @@ services: volumes: - /etc/localtime:/etc/localtime:ro - /path/to/appdata/qbitrr:/config # Config folder for qBitrr - - /path/to/sonarr/db:/sonarr.db:ro # This is only needed if you want episode search handling :ro means it is only ever mounted as a read-only folder, the script never needs more than read access - - /path/to/radarr/db:/radarr.db:ro # This is only needed if you want movie search handling, :ro means it is only ever mounted as a read-only folder, the script never needs more than read access - /path/to/completed/downloads/folder:/completed_downloads:rw # The script will ALWAYS require write permission in this folder if mounted, this folder is used to monitor completed downloads and if not present will cause the script to ignore downloaded file monitoring. - # Now just to make sure it is clean, when using this script in a docker you will need to ensure you config.toml values reflect the mounted folders.# - # For example, for your Sonarr.DatabaseFile value using the values above you'd add - # DatabaseFile = /sonarr.db/path/in/container/sonarr.db - # Because this is where you mounted it to + # Now just to make sure it is clean, when using this script in a docker you will need to ensure you config.toml values reflect the mounted folders. # The same would apply to Settings.CompletedDownloadFolder # e.g CompletedDownloadFolder = /completed_downloads/folder/in/container