wi1-bot 1.4.3__tar.gz → 1.4.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/.github/workflows/pypi-publish.yml +1 -1
  2. wi1-bot-1.4.5/.gitignore +8 -0
  3. wi1-bot-1.4.5/Dockerfile +20 -0
  4. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/PKG-INFO +2 -1
  5. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/README.md +1 -0
  6. wi1-bot-1.4.5/compose.yaml +16 -0
  7. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/pyproject.toml +1 -0
  8. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/_version.py +2 -2
  9. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/arr/radarr.py +2 -2
  10. wi1-bot-1.4.5/wi1_bot/scripts/rescan.py +57 -0
  11. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/transcoder/transcoder.py +28 -26
  12. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot.egg-info/PKG-INFO +2 -1
  13. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot.egg-info/SOURCES.txt +4 -0
  14. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot.egg-info/entry_points.txt +1 -0
  15. /wi1-bot-1.4.3/.gitignore → /wi1-bot-1.4.5/.dockerignore +0 -0
  16. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/.pre-commit-config.yaml +0 -0
  17. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/.vscode/launch.json +0 -0
  18. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/.vscode/settings.json +0 -0
  19. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/LICENSE +0 -0
  20. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/config.yaml.template +0 -0
  21. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/setup.cfg +0 -0
  22. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/tests/movie_downloaded.py +0 -0
  23. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/__init__.py +0 -0
  24. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/arr/__init__.py +0 -0
  25. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/arr/download.py +0 -0
  26. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/arr/episode.py +0 -0
  27. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/arr/movie.py +0 -0
  28. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/arr/sonarr.py +0 -0
  29. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/config.py +0 -0
  30. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/discord/__init__.py +0 -0
  31. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/discord/bot.py +0 -0
  32. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/discord/cogs/__init__.py +0 -0
  33. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/discord/cogs/movie.py +0 -0
  34. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/discord/cogs/series.py +0 -0
  35. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/discord/helpers.py +0 -0
  36. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/push.py +0 -0
  37. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/scripts/__init__.py +0 -0
  38. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/scripts/add_tag.py +0 -0
  39. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/scripts/start.py +0 -0
  40. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/scripts/transcode_item.py +0 -0
  41. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/transcoder/__init__.py +0 -0
  42. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/transcoder/transcode_queue.py +0 -0
  43. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot/webhook.py +0 -0
  44. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot.egg-info/dependency_links.txt +0 -0
  45. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot.egg-info/requires.txt +0 -0
  46. {wi1-bot-1.4.3 → wi1-bot-1.4.5}/wi1_bot.egg-info/top_level.txt +0 -0
@@ -3,7 +3,7 @@ name: Upload to PyPI
3
3
  on:
4
4
  push:
5
5
  tags:
6
- - "*"
6
+ - "v*"
7
7
 
8
8
  permissions:
9
9
  contents: read
@@ -0,0 +1,8 @@
1
+ config.yaml
2
+ tests/files/
3
+ log/
4
+
5
+ build/
6
+ dist/
7
+ *.egg-info/
8
+ _version.py
@@ -0,0 +1,20 @@
1
+ FROM python:3.10
2
+
3
+ RUN apt-get update && apt-get install -y ffmpeg
4
+
5
+ WORKDIR /app
6
+
7
+ COPY . .
8
+
9
+ RUN pip install .
10
+
11
+ ENV WB_CONFIG_PATH=/app/config/config.yaml
12
+ ENV WB_LOG_DIR=/app/logs
13
+
14
+ RUN mkdir -p config
15
+ RUN mkdir -p logs
16
+
17
+ EXPOSE 9000
18
+
19
+ ENTRYPOINT ["python", "-m", "wi1_bot.scripts.start"]
20
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wi1-bot
3
- Version: 1.4.3
3
+ Version: 1.4.5
4
4
  Summary: Discord bot for Radarr/Sonarr integration
5
5
  Home-page: https://github.com/wthueb/wi1-bot
6
6
  Author-email: William Huebner <wilhueb@gmail.com>
@@ -57,6 +57,7 @@ Requires Python >=3.10.
57
57
 
58
58
  ### TODO
59
59
 
60
+ - use sqlite
60
61
  - have config.discord.users be a dict with 'quotas' and 'name' for *arr tags
61
62
  - Better pushover notifications
62
63
  - Failures for pretty much everything
@@ -19,6 +19,7 @@ Requires Python >=3.10.
19
19
 
20
20
  ### TODO
21
21
 
22
+ - use sqlite
22
23
  - have config.discord.users be a dict with 'quotas' and 'name' for *arr tags
23
24
  - Better pushover notifications
24
25
  - Failures for pretty much everything
@@ -0,0 +1,16 @@
1
+ services:
2
+ wi1-bot:
3
+ container_name: wi1-bot
4
+ image: wthueb/wi1-bot
5
+ runtime: nvidia
6
+ deploy:
7
+ resources:
8
+ reservations:
9
+ devices:
10
+ - capabilities:
11
+ - gpu
12
+ environment:
13
+ - WB_CONFIG_PATH=/data/config.yaml
14
+ - WB_LOG_PATH=/data/logs
15
+ volumes:
16
+ - .:/data
@@ -27,6 +27,7 @@ Homepage = "https://github.com/wthueb/wi1-bot"
27
27
  wi1-bot = "wi1_bot.scripts.start:main"
28
28
  transcode-item = "wi1_bot.scripts.transcode_item:main"
29
29
  add-tag = "wi1_bot.scripts.add_tag:main"
30
+ rescan = "wi1_bot.scripts.rescan:main"
30
31
 
31
32
  [project.optional-dependencies]
32
33
  dev = [
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.4.3'
16
- __version_tuple__ = version_tuple = (1, 4, 3)
15
+ __version__ = version = '1.4.5'
16
+ __version_tuple__ = version_tuple = (1, 4, 5)
@@ -149,10 +149,10 @@ class Radarr:
149
149
  raise ValueError(f"no quality profile with the id {profile_id}")
150
150
 
151
151
  def rescan_movie(self, movie_id: int) -> None:
152
- self._radarr.post_command("RescanMovie", movieId=movie_id) # type: ignore
152
+ self._radarr.post_command("RescanMovie", movieId=movie_id)
153
153
 
154
154
  def refresh_movie(self, movie_id: int) -> None:
155
- self._radarr.post_command("RefreshMovie", movieIds=[movie_id]) # type: ignore
155
+ self._radarr.post_command("RefreshMovie", movieIds=[movie_id])
156
156
 
157
157
  def search_missing(self) -> None:
158
158
  self._radarr.post_command(name="MissingMoviesSearch")
@@ -0,0 +1,57 @@
1
+ import argparse
2
+ from time import sleep
3
+ from typing import cast
4
+
5
+ from wi1_bot.arr.radarr import Radarr
6
+ from wi1_bot.arr.sonarr import Sonarr
7
+ from wi1_bot.config import config
8
+
9
+
10
+ def rescan_radarr() -> None:
11
+ radarr = Radarr(config["radarr"]["url"], config["radarr"]["api_key"])
12
+
13
+ all_movies = radarr._radarr.get_movie()
14
+ assert isinstance(all_movies, list)
15
+ all_movies.sort(key=lambda m: cast(str, m["title"]))
16
+
17
+ for movie in all_movies:
18
+ assert isinstance(movie, dict)
19
+ print(f"Rescanning {movie['title']}...")
20
+ radarr.refresh_movie(movie["id"])
21
+ sleep(3)
22
+
23
+
24
+ def rescan_sonarr() -> None:
25
+ sonarr = Sonarr(config["sonarr"]["url"], config["sonarr"]["api_key"])
26
+
27
+ all_series = sonarr._sonarr.get_series()
28
+ assert isinstance(all_series, list)
29
+ all_series.sort(key=lambda s: cast(str, s["title"]))
30
+
31
+ for series in all_series:
32
+ assert isinstance(series, dict)
33
+ print(f"Rescanning {series['title']}...")
34
+ sonarr.rescan_series(series["id"])
35
+ sleep(5)
36
+
37
+
38
+ def main() -> None:
39
+ parser = argparse.ArgumentParser(description="Rescan all movies/shows")
40
+
41
+ parser.add_argument(
42
+ "service", nargs="?", choices=["radarr", "sonarr"], help="radarr or sonarr"
43
+ )
44
+
45
+ args = parser.parse_args()
46
+
47
+ if args.service == "radarr":
48
+ rescan_radarr()
49
+ elif args.service == "sonarr":
50
+ rescan_sonarr()
51
+ else:
52
+ rescan_radarr()
53
+ rescan_sonarr()
54
+
55
+
56
+ if __name__ == "__main__":
57
+ main()
@@ -46,17 +46,10 @@ class Transcoder:
46
46
  continue
47
47
 
48
48
  try:
49
- self._do_transcode(item)
50
- queue.remove(item)
51
- except SignalInterrupt:
52
- # retry when restarted
53
- pass
54
- except FileNotFoundError:
55
- # don't retry
56
- queue.remove(item)
57
- except UnknownError:
58
- # don't retry
59
- queue.remove(item)
49
+ remove = self._do_transcode(item)
50
+
51
+ if remove:
52
+ queue.remove(item)
60
53
  except Exception:
61
54
  self.logger.warning(
62
55
  "got exception when trying to transcode", exc_info=True
@@ -64,14 +57,14 @@ class Transcoder:
64
57
 
65
58
  sleep(3)
66
59
 
67
- def _do_transcode(self, item: TranscodeItem) -> None:
60
+ def _do_transcode(self, item: TranscodeItem) -> bool:
68
61
  path = pathlib.Path(item.path)
69
62
 
70
- self.logger.debug(f"attempting to transcode {path.name}")
63
+ self.logger.info(f"attempting to transcode {path.name}")
71
64
 
72
65
  if path.suffix == ".avi":
73
- self.logger.debug(f"cannot transcode {path.name}: .avi not supported")
74
- return
66
+ self.logger.info(f"cannot transcode {path.name}: .avi not supported")
67
+ return True
75
68
 
76
69
  # push.send(f"{basename}", title="starting transcode")
77
70
 
@@ -103,26 +96,32 @@ class Transcoder:
103
96
  tmp_log_path = tmp_folder / "wi1_bot.transcoder.log"
104
97
 
105
98
  with open(tmp_log_path, "w") as ffmpeg_log_file:
106
- for line in proc.stdout: # type: ignore
99
+ assert proc.stdout is not None
100
+ for line in proc.stdout:
107
101
  ffmpeg_log_file.write(line)
108
102
  last_output = line.strip()
109
103
 
110
104
  status = proc.wait()
111
105
 
112
106
  if status != 0:
113
- self.logger.error(f"ffmpeg failed (status {status}): {last_output}")
114
-
115
- if "No such file or directory" in last_output:
116
- self.logger.debug(
107
+ if "Error opening input files" in last_output:
108
+ self.logger.info(
117
109
  f"file does not exist: {path}, skipping transcoding"
118
110
  )
119
- raise FileNotFoundError
111
+ return True
120
112
 
121
113
  if "received signal 15" in last_output:
122
- self.logger.debug(
114
+ self.logger.info(
123
115
  f"transcoding interrupted by signal: {path}, will retry"
124
116
  )
125
- raise SignalInterrupt
117
+ return False
118
+
119
+ if "cannot open shared object file" in last_output:
120
+ self.logger.error(
121
+ "ffmpeg error: missing shared object file, will retry"
122
+ )
123
+ push.send(f"ffmpeg error: {last_output}", title="ffmpeg error")
124
+ return False
126
125
 
127
126
  perm_log_path = tmp_folder / f"{path.stem}.log"
128
127
 
@@ -133,6 +132,8 @@ class Transcoder:
133
132
  perm_log_path.parent.mkdir(parents=True, exist_ok=True)
134
133
 
135
134
  shutil.copy(tmp_log_path, perm_log_path)
135
+
136
+ self.logger.error(f"ffmpeg failed (status {status}): {last_output}")
136
137
  self.logger.error(f"log file: {perm_log_path}")
137
138
 
138
139
  push.send(
@@ -140,7 +141,7 @@ class Transcoder:
140
141
  title="transcoding error",
141
142
  )
142
143
 
143
- raise UnknownError
144
+ return True
144
145
 
145
146
  new_path = path.parent / transcode_to.name
146
147
 
@@ -150,7 +151,7 @@ class Transcoder:
150
151
  )
151
152
 
152
153
  transcode_to.unlink()
153
- return
154
+ return True
154
155
 
155
156
  shutil.move(transcode_to, new_path)
156
157
  path.unlink()
@@ -160,8 +161,9 @@ class Transcoder:
160
161
  self.logger.info(f"transcoded: {path.name} -> {new_path.name}")
161
162
  # push.send(f"{path.name} -> {new_path.name}", title="file transcoded")
162
163
 
164
+ return True
165
+
163
166
  def _rescan_content(self, item: TranscodeItem, new_path: str) -> None:
164
- # FIXME: don't hardcode library paths (config)
165
167
  if item.content_id is not None:
166
168
  if new_path.startswith(config["radarr"]["root_folder"]):
167
169
  self.radarr.rescan_movie(item.content_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wi1-bot
3
- Version: 1.4.3
3
+ Version: 1.4.5
4
4
  Summary: Discord bot for Radarr/Sonarr integration
5
5
  Home-page: https://github.com/wthueb/wi1-bot
6
6
  Author-email: William Huebner <wilhueb@gmail.com>
@@ -57,6 +57,7 @@ Requires Python >=3.10.
57
57
 
58
58
  ### TODO
59
59
 
60
+ - use sqlite
60
61
  - have config.discord.users be a dict with 'quotas' and 'name' for *arr tags
61
62
  - Better pushover notifications
62
63
  - Failures for pretty much everything
@@ -1,7 +1,10 @@
1
+ .dockerignore
1
2
  .gitignore
2
3
  .pre-commit-config.yaml
4
+ Dockerfile
3
5
  LICENSE
4
6
  README.md
7
+ compose.yaml
5
8
  config.yaml.template
6
9
  pyproject.toml
7
10
  setup.cfg
@@ -34,6 +37,7 @@ wi1_bot/discord/cogs/movie.py
34
37
  wi1_bot/discord/cogs/series.py
35
38
  wi1_bot/scripts/__init__.py
36
39
  wi1_bot/scripts/add_tag.py
40
+ wi1_bot/scripts/rescan.py
37
41
  wi1_bot/scripts/start.py
38
42
  wi1_bot/scripts/transcode_item.py
39
43
  wi1_bot/transcoder/__init__.py
@@ -1,4 +1,5 @@
1
1
  [console_scripts]
2
2
  add-tag = wi1_bot.scripts.add_tag:main
3
+ rescan = wi1_bot.scripts.rescan:main
3
4
  transcode-item = wi1_bot.scripts.transcode_item:main
4
5
  wi1-bot = wi1_bot.scripts.start:main
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes