episoder 0.10.1__tar.gz → 0.11.1__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 (80) hide show
  1. episoder-0.11.1/.gitignore +2 -0
  2. episoder-0.11.1/.gitlab-ci.yml +124 -0
  3. episoder-0.11.1/CHANGELOG +222 -0
  4. {episoder-0.10.1 → episoder-0.11.1}/PKG-INFO +6 -2
  5. {episoder-0.10.1 → episoder-0.11.1}/episoder/database.py +19 -18
  6. {episoder-0.10.1 → episoder-0.11.1}/episoder/episoder.py +38 -36
  7. {episoder-0.10.1 → episoder-0.11.1}/episoder/main.py +68 -84
  8. episoder-0.11.1/episoder/output.py +169 -0
  9. {episoder-0.10.1 → episoder-0.11.1}/episoder/sources/__init__.py +1 -1
  10. {episoder-0.10.1 → episoder-0.11.1}/episoder/sources/epguides.py +1 -1
  11. {episoder-0.10.1 → episoder-0.11.1}/episoder/sources/parser.py +1 -1
  12. {episoder-0.10.1 → episoder-0.11.1}/episoder/sources/thetvdb.py +3 -3
  13. {episoder-0.10.1 → episoder-0.11.1}/episoder/sources/tvcom.py +1 -1
  14. {episoder-0.10.1 → episoder-0.11.1}/episoder/version.py +2 -2
  15. episoder-0.11.1/episoder.1 +247 -0
  16. {episoder-0.10.1 → episoder-0.11.1}/episoder.egg-info/PKG-INFO +6 -2
  17. episoder-0.11.1/episoder.egg-info/SOURCES.txt +76 -0
  18. {episoder-0.10.1 → episoder-0.11.1}/episoder.egg-info/requires.txt +1 -1
  19. episoder-0.11.1/examples/home.episoder +30 -0
  20. {episoder-0.10.1 → episoder-0.11.1}/setup.cfg +1 -1
  21. episoder-0.11.1/test/__init__.py +0 -0
  22. episoder-0.11.1/test/fixtures/epguides_48_hours_mistery.html +399 -0
  23. episoder-0.11.1/test/fixtures/epguides_american_idol.html +872 -0
  24. episoder-0.11.1/test/fixtures/epguides_bsg.html +538 -0
  25. episoder-0.11.1/test/fixtures/epguides_buzzcocks.html +464 -0
  26. episoder-0.11.1/test/fixtures/epguides_doctor_who.html +842 -0
  27. episoder-0.11.1/test/fixtures/epguides_eureka.html +362 -0
  28. episoder-0.11.1/test/fixtures/epguides_futurama.html +407 -0
  29. episoder-0.11.1/test/fixtures/epguides_house.html +490 -0
  30. episoder-0.11.1/test/fixtures/epguides_kr2008.html +256 -0
  31. episoder-0.11.1/test/fixtures/epguides_lost.html +478 -0
  32. episoder-0.11.1/test/fixtures/epguides_lost_html.html +602 -0
  33. episoder-0.11.1/test/fixtures/epguides_lower_decks.html +340 -0
  34. episoder-0.11.1/test/fixtures/epguides_midsomer_murders.html +398 -0
  35. episoder-0.11.1/test/fixtures/epguides_picard.html +380 -0
  36. episoder-0.11.1/test/fixtures/epguides_test_iso_8859_1.html +138 -0
  37. episoder-0.11.1/test/fixtures/tvdb_error_404.json +3 -0
  38. episoder-0.11.1/test/fixtures/tvdb_search_frasier.json +10 -0
  39. episoder-0.11.1/test/fixtures/tvdb_search_friends.json +28 -0
  40. episoder-0.11.1/test/fixtures/tvdb_test_show_260.json +1 -0
  41. episoder-0.11.1/test/fixtures/tvdb_test_show_260_1_eps.json +19 -0
  42. episoder-0.11.1/test/fixtures/tvdb_test_show_261.json +1 -0
  43. episoder-0.11.1/test/fixtures/tvdb_test_show_261_1_eps.json +23 -0
  44. episoder-0.11.1/test/fixtures/tvdb_test_show_261_2_eps.json +23 -0
  45. episoder-0.11.1/test/fixtures/tvdb_test_show_261_3_eps.json +18 -0
  46. episoder-0.11.1/test/fixtures/tvdb_test_show_262.json +1 -0
  47. episoder-0.11.1/test/fixtures/tvdb_test_show_262_1_eps.json +20 -0
  48. episoder-0.11.1/test/fixtures/tvdb_test_show_263.json +1 -0
  49. episoder-0.11.1/test/fixtures/tvdb_test_show_268156.json +1 -0
  50. episoder-0.11.1/test/fixtures/tvdb_test_show_268156_1_eps.json +520 -0
  51. episoder-0.11.1/test/fixtures/tvdb_test_show_295640.json +1 -0
  52. episoder-0.11.1/test/fixtures/tvdb_test_show_295640_1_eps.json +1 -0
  53. episoder-0.11.1/test/fixtures/tvdb_test_show_295640_2_eps.json +1 -0
  54. episoder-0.11.1/test/fixtures/tvdb_test_show_73739.json +1 -0
  55. episoder-0.11.1/test/fixtures/tvdb_test_show_73739_1_eps.json +14 -0
  56. episoder-0.11.1/test/fixtures/tvdb_test_show_75397.json +1 -0
  57. episoder-0.11.1/test/fixtures/tvdb_test_show_75397_1_eps.json +1 -0
  58. {episoder-0.10.1 → episoder-0.11.1}/test/test_database.py +68 -68
  59. {episoder-0.10.1 → episoder-0.11.1}/test/test_epguides_parser.py +27 -23
  60. {episoder-0.10.1 → episoder-0.11.1}/test/test_episode.py +15 -15
  61. {episoder-0.10.1 → episoder-0.11.1}/test/test_metainfo.py +1 -1
  62. {episoder-0.10.1 → episoder-0.11.1}/test/test_migration.py +113 -125
  63. {episoder-0.10.1 → episoder-0.11.1}/test/test_notify.py +50 -77
  64. episoder-0.11.1/test/test_output.py +223 -0
  65. {episoder-0.10.1 → episoder-0.11.1}/test/test_show.py +14 -14
  66. {episoder-0.10.1 → episoder-0.11.1}/test/test_sources.py +4 -1
  67. {episoder-0.10.1 → episoder-0.11.1}/test/test_tvcom.py +2 -2
  68. {episoder-0.10.1 → episoder-0.11.1}/test/test_tvdb.py +29 -28
  69. episoder-0.11.1/test/util.py +60 -0
  70. episoder-0.10.1/episoder/output.py +0 -187
  71. episoder-0.10.1/episoder.egg-info/SOURCES.txt +0 -33
  72. episoder-0.10.1/test/test_output.py +0 -250
  73. {episoder-0.10.1 → episoder-0.11.1}/COPYING +0 -0
  74. {episoder-0.10.1 → episoder-0.11.1}/README.md +0 -0
  75. {episoder-0.10.1 → episoder-0.11.1}/episoder/__init__.py +0 -0
  76. {episoder-0.10.1 → episoder-0.11.1}/episoder.egg-info/dependency_links.txt +0 -0
  77. {episoder-0.10.1 → episoder-0.11.1}/episoder.egg-info/entry_points.txt +0 -0
  78. {episoder-0.10.1 → episoder-0.11.1}/episoder.egg-info/top_level.txt +0 -0
  79. {episoder-0.10.1 → episoder-0.11.1}/pyproject.toml +0 -0
  80. {episoder-0.10.1 → episoder-0.11.1}/setup.py +0 -0
@@ -0,0 +1,2 @@
1
+ *.pyc
2
+ *.swp
@@ -0,0 +1,124 @@
1
+ stages:
2
+ - test
3
+ - deploy
4
+
5
+ .jammy_image: &jammy
6
+ image: registry.gitlab.com/schabe/episoder:ubuntu-jammy
7
+
8
+ .bookworm_image: &bookworm
9
+ image: registry.gitlab.com/schabe/episoder:debian-bookworm
10
+
11
+ .trixie_image: &trixie
12
+ image: registry.gitlab.com/schabe/episoder:debian-trixie
13
+
14
+ .noble_image: &noble
15
+ image: registry.gitlab.com/schabe/episoder:ubuntu-noble
16
+
17
+ .test_template: &test_installed_setuptools
18
+ stage: test
19
+ script:
20
+ - python3 setup.py install
21
+ - python3 -m pytest --junitxml=report.xml --cov=episoder/
22
+ - python3 -m coverage xml
23
+ artifacts:
24
+ reports:
25
+ junit: report.xml
26
+ coverage_report:
27
+ coverage_format: cobertura
28
+ path: coverage.xml
29
+
30
+ .test_template: &test
31
+ stage: test
32
+ script:
33
+ - python3 -m pytest --junitxml=report.xml --cov=episoder/
34
+ - python3 -m coverage xml
35
+ artifacts:
36
+ reports:
37
+ junit: report.xml
38
+ coverage_report:
39
+ coverage_format: cobertura
40
+ path: coverage.xml
41
+
42
+ .deploy_setuptools_template: &deploy_setuptools
43
+ stage: deploy
44
+ script:
45
+ - python3 setup.py install
46
+ - python3 setup.py sdist
47
+ - python3 setup.py bdist_egg
48
+ - episoder --version
49
+
50
+ .deploy_pip_template: &deploy_pip
51
+ stage: deploy
52
+ script:
53
+ - pip install .
54
+ - episoder --version
55
+
56
+ .deploy_setuptools_venv_template: &deploy_pip_venv
57
+ stage: deploy
58
+ script:
59
+ - python3 -m venv --system-site-packages venv
60
+ - venv/bin/python3 setup.py install
61
+ - venv/bin/python3 setup.py sdist
62
+ - venv/bin/python3 setup.py bdist_egg
63
+ - venv/bin/episoder --version
64
+
65
+ .deploy_pip_venv_template: &deploy_pip_venv
66
+ stage: deploy
67
+ script:
68
+ - python3 -m venv --system-site-packages venv
69
+ - venv/bin/pip install .
70
+ - venv/bin/episoder --version
71
+
72
+ py310-jammy:test:
73
+ <<: *jammy
74
+ <<: *test
75
+
76
+ py310-jammy-setuptools:deploy:
77
+ <<: *jammy
78
+ <<: *deploy_setuptools
79
+
80
+ py310-jammy-pip:deploy:
81
+ <<: *jammy
82
+ <<: *deploy_pip
83
+
84
+ py311-bookworm:test:
85
+ <<: *bookworm
86
+ <<: *test
87
+
88
+ py311-bookworm-setuptools:deploy:
89
+ needs: ["py311-bookworm:test"]
90
+ <<: *bookworm
91
+ <<: *deploy_setuptools
92
+
93
+ py311-bookworm-pip:deploy:
94
+ needs: ["py311-bookworm:test"]
95
+ <<: *bookworm
96
+ <<: *deploy_pip_venv
97
+
98
+ py312-noble:test:
99
+ <<: *noble
100
+ <<: *test
101
+
102
+ py312-noble-setuptools:deploy:
103
+ needs: ["py312-noble:test"]
104
+ <<: *noble
105
+ <<: *deploy_setuptools
106
+
107
+ py312-noble-pip:deploy:
108
+ needs: ["py312-noble:test"]
109
+ <<: *noble
110
+ <<: *deploy_pip_venv
111
+
112
+ py313-trixie:test:
113
+ <<: *trixie
114
+ <<: *test
115
+
116
+ py313-trixie-setuptools:deploy:
117
+ needs: ["py313-trixie:test"]
118
+ <<: *trixie
119
+ <<: *deploy_setuptools
120
+
121
+ py313-trixie-pip:deploy:
122
+ needs: ["py313-trixie:test"]
123
+ <<: *trixie
124
+ <<: *deploy_pip_venv
@@ -0,0 +1,222 @@
1
+ == 0.11.1 (8 Apr 2025) ==
2
+ * Use Python's EmailMessage class when sending e-mail
3
+
4
+ == 0.11.0 (20 Jul 2024) ==
5
+ * Port to sqlalchemy 2.0
6
+
7
+ == 0.10.1 (17 May 2023) ==
8
+ * tvdb: Fix a crash on shows with an empty 'data' field
9
+ * epguides: Fix parsing of cancelled/ended shows
10
+
11
+ == 0.10.0 (25 Mar 2023) ==
12
+ * Fix a crash when trying to render shows without a name
13
+ * Explicitly depend on sqlalchemy 1.3/1.4
14
+ * Fix code that sets the database schema version
15
+ * Rename package from pyepisoder to episoder
16
+ * Update distribution scripts
17
+
18
+ == 0.9.2 (1 Jan 2021) ==
19
+ * Fix a crash when trying to render episodes without production number
20
+ * Use appropriate colors when showing episodes from a past date
21
+ * Fix formatting of show names in list of shows
22
+ * Accept empty episode lines in epguide HTML format parser
23
+
24
+ == 0.9.1 (11 Oct 2020) ==
25
+ * Fix setup.py
26
+
27
+ == 0.9.0 (11 Oct 2020) ==
28
+ * Add support for new HTML format on epguides.com (thanks to Jethro Beekman)
29
+
30
+ == 0.8.5 (28 Mar 2020) ==
31
+ * Fix a small bug in setup.py
32
+
33
+ == 0.8.4 (28 Mar 2020) ==
34
+ * Fix loading of shows without episodes from TVDB
35
+ * Fix loading of episodes with invalid date (0000-00-00)
36
+
37
+ == 0.8.3 (27 Sep 2017) ==
38
+ * Fixed a crash related to unexpected null values in TVDB JSON data
39
+ * Use correct path for example config file in setup.py (fixes #7)
40
+
41
+ == 0.8.2 (7 Jul 2017) ==
42
+ * Fixed a crash when rendering episodes without a title
43
+
44
+ == 0.8.1 (24 Jun 2017) ==
45
+ * Handle failures to load TVDB API pages
46
+
47
+ == 0.8.0 (7 Jun 2017) ==
48
+ * Switched from distutils to setuptools
49
+ * We now use the new TVDB JSON API
50
+ * Rewrote the epguides.com parser in pure Python
51
+ * Rewrote large parts of the database code
52
+ * Episoder should now work fully with Python 3
53
+ * The update command can now be limited to a fixed number of shows
54
+ * Updated man page
55
+ * Thanks to Chris Spencer (chrisspen) for his contributions
56
+ * Added option to only list active shows (chrisspen)
57
+ * Added the number of episodes to the list of shows (chrisspen)
58
+ * Added email notification feature via 'episoder notify' (chrisspen)
59
+
60
+ == 0.7.3 (7 Nov 2015) ==
61
+ * Fixed a syntax error in the list of shows
62
+ * Fixed season number parsing for epguides.com (thanks to jethrogb)
63
+
64
+ == 0.7.2 (26 Sep 2015) ==
65
+ * Dropped unneeded dependency on BeautifulSoup
66
+ * Fixed episode number parsing for epguides.com (thanks to jethrogb)
67
+ * Improved python3 compatibility
68
+
69
+ == 0.7.1 (27 Apr 2014) ==
70
+ * Accept unicode characters in prodnum fields
71
+
72
+ == 0.7.0 (14 Oct 2013) ==
73
+ * Added support for TheTVDB.com
74
+ * Removed support for tv.com
75
+ * Changed command-line arguments. See the man page for details
76
+ * Removed the ability to specify shows in the configuration file
77
+ * Reorganized the command line options. Check the documentation for details
78
+ * Updated the epguides.com parser to ignore trailer links in episode names
79
+ * Fixed some more encoding issues in the console renderer
80
+ * Changed license to GPLv3
81
+
82
+ == 0.6.5 (6 Oct 2010) ==
83
+ * Updated the epguides.com parser to ignore links to trailers in episode names
84
+
85
+ == 0.6.4 (25 Jun 2010) ==
86
+ * Filter javascript from tv.com pages because it confuses beautifulsoup 3.1
87
+
88
+ == 0.6.3 (23 Jun 2010) ==
89
+ * The epguides parser now properly reads the season number for more shows
90
+
91
+ == 0.6.2 (08 Nov 2009) ==
92
+ * Updated tv.com parser (show names need to be lower-case now!)
93
+ * Added -S option to specify a show id for (forced) updates
94
+
95
+ == 0.6.1 (26 Aug 2009) ==
96
+ * Episoder can now talk to any database supported by sqlalchemy (see README)
97
+ * Tell awk to process utf8 (for epguides parser)
98
+ * Catch (and properly log) some more HTTP exceptions
99
+
100
+ == 0.6.0 (8 Aug 2009) ==
101
+ * Episoder now keeps a persistent database of shows and episodes
102
+ * Added new options (-L, -a, -r, -E, -D) to manage the database
103
+ * The parsers now know whether a show is still running or not
104
+ * Active (currently running) shows are updated every two days
105
+ * Inactive (ended) shows are updated every two weeks
106
+ * The new -B command overrides those intervals and forces an update
107
+ * Show names can no longer be overridden in the configuration file
108
+ * Don't report an error when no sources are defined in the config file
109
+ * The tv.com parser now also accepts urls containing '!', '+' or '%'
110
+ * The -d option now also accepts the number of days instead of a fixed date
111
+ * Documentation updated
112
+
113
+ == 0.5.3 (26 Jul 2009) ==
114
+ * Added support for another epguides formatting style (used eg. for "Eureka")
115
+ * Added option to suppress colors in console output
116
+ * Added a filter to remove invalid characters from epguides content
117
+ * Fixed some other parsing issues in the epguides parser
118
+ * Improved compatibility with older python versions
119
+
120
+ == 0.5.2 (30 Jun 2009) ==
121
+ * Reworked parts of the code to improve compatibility with older libraries
122
+ * Fixed some encoding issues in the console renderer
123
+
124
+ == 0.5.1 (23 Jun 2009) ==
125
+ * Thanks to Philip Douglass for his bug reports and patches
126
+ * The epguides parser now properly closes all file handles and works with mawk
127
+ * The tv.com parser would crash on missing production/season/episode numbers
128
+ * The tv.com parser now accepts urls that contain a dash
129
+ * Switched to using sqlalchemy to access the database
130
+ * Properly implemented the user-agent configuration option
131
+ * Removed old FAQ
132
+ * Fixed some text encoding issues
133
+ * Small fixes to the man page
134
+
135
+ == 0.5.0 (18 May 2009) ==
136
+ * Rewrote the whole thing in python
137
+ * Wrote a brand new tv.com parser
138
+ * Small fixes in the epguides.com parser
139
+ * Parsed data is now stored in an sqlite3 database
140
+
141
+ == 0.4.7 (24 Feb 2007) ==
142
+ * Fixed a parsing issue introduced with 0.4.6
143
+
144
+ == 0.4.6 (24 Feb 2007) ==
145
+ * Discovered yet another formatting possibility on epguides.com (used for
146
+ instance for the show "Midsomer Murders") and adapted the parser accordingly
147
+ * Slightly updated the man page
148
+
149
+ == 0.4.5 (23 Jan 2007) ==
150
+ * Fixed some epguides parser issues
151
+
152
+ == 0.4.4 (19 Nov 2006) ==
153
+ * Fixed the -i option when building the database (did the exact opposite of
154
+ what it was supposed to do)
155
+
156
+ == 0.4.3 (24 Oct 2006) ==
157
+ * We now actually honor the -i option when building the database
158
+ * Added the possibility to override a show's name in the config file. This was needed for the new webisoder, a web-based episoder. Find out more about it at http://www.webisoder.net/
159
+ * Oops, forgot to update episoder's version number in the last release...
160
+ * Fixed a bug in the new epguides.com parser which would not drop episodes without a date
161
+
162
+ == 0.4.2 (21 Oct 2006) ==
163
+ * Don't ignore the config file passed with -c anymore when building the db
164
+ * Added parser for epguides.com (closes #38)
165
+
166
+ == 0.4.1 (20 Apr 2006) ==
167
+ * Did some minor internal changes to get episoder to work on non-linux systems
168
+ * Removed long options
169
+ * Hotfix for the tv.com website changes
170
+
171
+ == 0.4.0 (3 Dec 2005) ==
172
+ * Re-organized options: -vv is now -w, -rc is now -c (or --rc), -nd is gone (use --nodate), -sa is gone (use -s and --nodate instead), -i is gone (see below)
173
+ * Search is now alway case-insensitive
174
+ * If no action is specified, episoder now defaults to showlist
175
+ * Removed the tvtome dummy plugin
176
+
177
+ == 0.3.0 (9 Aug 2005) ==
178
+ * Majeed Goldrick joined the project
179
+ * Moved parts of the website to sourceforge
180
+ * Removed a 1024-episodes-limit in sortlist
181
+ * Added search options
182
+ * Switched to awk to remove old episodes (>600 times faster)
183
+ * Switched to awk for tv.com parsing (>10 times faster)
184
+ * Removed the debian files from the main distribution
185
+ * Added a couple of tests
186
+
187
+ == 0.2.5 (19 Jul 2005) ==
188
+ * Fixed an alignment problem in showlist (closes #25)
189
+ * Fixed a date-calculation issue in showlist (closes #23)
190
+ * Fixed a documentation mistake about the format string (closes #21)
191
+ * Added support for nun-numeric episode numbers (closes #24)
192
+
193
+ == 0.2.4 (19 Jul 2005) ==
194
+ * Re-arranged the code to remove a 3-episodes-limit in showlist (closes #19)
195
+ * Added command line option to specify the number of days to show (closes #20)
196
+ * Brought back season-episode information in the tvcom plugin (closes #15)
197
+ * Added the possibility to customize the output format (closes #16)
198
+ * Added a small FAQ
199
+ * Thanks to Majeed Goldrick for all the bug reports and feature suggestions
200
+ * Cosmetic changes
201
+
202
+ == 0.2.3 (23 Jun 2005) ==
203
+ * The tvcom plugin now uses the new "All Seasons" link
204
+ * Cosmetic changes
205
+
206
+ == 0.2.2 (17 Jun 2005) ==
207
+ * Added LICENSE file
208
+ * install.sh now correctly changes the EPISODER_HOME value
209
+ * Small fixes in the verbose output
210
+ * Created tvcom plugin
211
+ * Replaced all functionality from the tvtome plugin with a warning
212
+ * Now all the available modules are actually loaded
213
+ * Some minor changes in the man page
214
+
215
+ == 0.2.1 (4 Jan 2005) ==
216
+ * The tvtome plugin now understands UNAIRED episodes
217
+ * Improved the builddb script's debug output
218
+ * Fixed an issue that would print errors for shows with no upcoming episodes
219
+ * The debian files are now part of the main distribution
220
+
221
+ == 0.2.0 (13 Sep 2004) ==
222
+ * Initial public release
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: episoder
3
- Version: 0.10.1
3
+ Version: 0.11.1
4
4
  Summary: TV episode notifier
5
5
  Home-page: https://code.ott.net/episoder/
6
6
  Author: Stefan Ott
@@ -17,6 +17,10 @@ Classifier: Topic :: Multimedia :: Video
17
17
  Requires-Python: >=3.6
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: COPYING
20
+ Requires-Dist: argparse
21
+ Requires-Dist: requests
22
+ Requires-Dist: beautifulsoup4
23
+ Requires-Dist: sqlalchemy>=1.3
20
24
 
21
25
  # episoder
22
26
 
@@ -1,6 +1,6 @@
1
1
  # episoder, https://code.ott.net/episoder
2
2
  #
3
- # Copyright (C) 2004-2023 Stefan Ott. All rights reserved.
3
+ # Copyright (C) 2004-2024 Stefan Ott. All rights reserved.
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
@@ -21,8 +21,7 @@ from datetime import datetime
21
21
 
22
22
  from sqlalchemy import Column, Integer, Text, Date, ForeignKey, Sequence
23
23
  from sqlalchemy import DateTime, Boolean
24
- from sqlalchemy.ext.declarative import declarative_base
25
- from sqlalchemy.orm import relationship, backref
24
+ from sqlalchemy.orm import backref, declarative_base, relationship
26
25
 
27
26
  Base = declarative_base()
28
27
 
@@ -36,14 +35,14 @@ class Show(Base):
36
35
  __tablename__ = "shows"
37
36
 
38
37
  id = Column("show_id", Integer, Sequence("shows_show_id_seq"),
39
- primary_key=True)
38
+ primary_key=True)
40
39
  name = Column("show_name", Text)
41
40
  url = Column(Text, unique=True)
42
41
  updated = Column(DateTime)
43
42
  enabled = Column(Boolean)
44
43
  status = Column(Integer, default=RUNNING)
45
44
 
46
- def __init__(self, name, url=u"", updated=datetime.fromtimestamp(0)):
45
+ def __init__(self, name, url="", updated=datetime.fromtimestamp(0)):
47
46
 
48
47
  self.name = name
49
48
  self.url = url
@@ -54,11 +53,11 @@ class Show(Base):
54
53
 
55
54
  def __str__(self):
56
55
 
57
- return "Show: %s" % self.name
56
+ return f"Show: {self.name}"
58
57
 
59
58
  def __repr__(self):
60
59
 
61
- return 'Show(u"%s", u"%s")' % (self.name, self.url)
60
+ return f'Show("{self.name}", "{self.url}")'
62
61
 
63
62
  def __eq__(self, other):
64
63
 
@@ -84,6 +83,7 @@ class Episode(Base):
84
83
  notified = Column(Date)
85
84
  show = relationship(Show, backref=backref("episodes", cascade="all"))
86
85
 
86
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
87
87
  def __init__(self, name, season, episode, aired, prodnum, total):
88
88
 
89
89
  self.title = name
@@ -96,26 +96,27 @@ class Episode(Base):
96
96
  def __lt__(self, other):
97
97
 
98
98
  return ((self.season < other.season)
99
- or ((self.season == other.season)
100
- and (self.episode < other.episode)))
99
+ or ((self.season == other.season)
100
+ and (self.episode < other.episode)))
101
101
 
102
102
  def __str__(self):
103
103
 
104
- return "%s %dx%02d: %s" % (self.show.name, self.season,
105
- self.episode, self.title)
104
+ return f"{self.show.name} {self.season}x{self.episode:02d}: " \
105
+ f"{self.title}"
106
106
 
107
107
  def __repr__(self):
108
108
 
109
- return 'Episode(u"%s", %d, %d, date(%d, %d, %d), u"%s", %d)' % (
110
- self.title, self.season, self.episode,
111
- self.airdate.year, self.airdate.month,
112
- self.airdate.day, self.prodnum, self.totalnum)
109
+ d = self.airdate
110
+
111
+ return f'Episode("{self.title}", {self.season}, {self.episode}, ' \
112
+ f'date({d.year}, {d.month}, {d.day}), "{self.prodnum}", ' \
113
+ f'{self.totalnum})'
113
114
 
114
115
  def __eq__(self, other):
115
116
 
116
117
  return (self.show_id == other.show_id
117
- and self.season == other.season
118
- and self.episode == other.episode)
118
+ and self.season == other.season
119
+ and self.episode == other.episode)
119
120
 
120
121
 
121
122
  class Meta(Base):
@@ -126,7 +127,7 @@ class Meta(Base):
126
127
 
127
128
  def __str__(self):
128
129
 
129
- return "Meta: %s = %s" % (self.key, self.value)
130
+ return f"Meta: {self.key} = {self.value}"
130
131
 
131
132
  def __repr__(self):
132
133
 
@@ -1,6 +1,6 @@
1
1
  # episoder, https://code.ott.net/episoder
2
2
  #
3
- # Copyright (C) 2004-2023 Stefan Ott. All rights reserved.
3
+ # Copyright (C) 2004-2024 Stefan Ott. All rights reserved.
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@ import logging
19
19
  from datetime import date, timedelta
20
20
 
21
21
  import sqlite3
22
- from sqlalchemy import Table, MetaData, create_engine, or_, and_
23
- from sqlalchemy.orm import create_session
22
+ from sqlalchemy import create_engine, or_, and_, select, inspect
23
+ from sqlalchemy.orm import sessionmaker
24
24
 
25
25
  from .database import Episode, Show, Meta
26
26
 
@@ -37,20 +37,21 @@ class Database:
37
37
 
38
38
  def __str__(self):
39
39
 
40
- return "Episoder Database at %s" % self._path
40
+ return f"Episoder Database at {self._path}"
41
41
 
42
42
  def __repr__(self):
43
43
 
44
- return "Database(%s)" % self._path
44
+ return f"Database({self._path})"
45
45
 
46
46
  def _initdb(self):
47
47
 
48
+ inspector = inspect(self.engine)
49
+
48
50
  # Initialize the database if all tables are missing
49
- tables = [Show, Episode, Meta]
50
- tables = map(lambda x: x.__table__.exists, tables)
51
- found = [x for x in tables if x(bind=self.engine)]
51
+ names = [Show, Episode, Meta]
52
+ tables = [t for t in names if inspector.has_table(t.__table__.name)]
52
53
 
53
- if len(found) < 1:
54
+ if len(tables) < 1:
54
55
  Show.__table__.create(bind=self.engine)
55
56
  Episode.__table__.create(bind=self.engine)
56
57
  Meta.__table__.create(bind=self.engine)
@@ -61,36 +62,35 @@ class Database:
61
62
  if self._path.find("://") > -1:
62
63
  self.engine = create_engine(self._path)
63
64
  else:
64
- self.engine = create_engine("sqlite:///%s" % self._path)
65
+ self.engine = create_engine(f"sqlite:///{self._path}")
65
66
 
66
- self.conn = self.engine.connect()
67
- self.metadata = MetaData()
68
- self.metadata.bind = self.engine
69
- self.session = create_session(bind=self.engine)
67
+ sm = sessionmaker(self.engine)
68
+ self.session = sm()
70
69
  self.session.begin()
71
70
 
72
71
  def close(self):
73
72
 
74
73
  self.session.commit()
75
74
  self.session.close()
76
- self.conn.close()
77
75
  self.engine.dispose()
78
76
 
79
77
  def set_schema_version(self, version):
80
78
 
81
79
  meta = Meta()
82
80
  meta.key = "schema"
83
- meta.value = "%d" % version
81
+ meta.value = f"{version}"
84
82
 
85
83
  with self.session.begin_nested():
86
84
  self.session.merge(meta)
87
85
 
88
86
  def get_schema_version(self):
89
87
 
90
- if not Meta.__table__.exists(bind=self.engine):
88
+ inspector = inspect(self.engine)
89
+ if not inspector.has_table(Meta.__table__.name):
91
90
  return 1
92
91
 
93
- res = self.session.query(Meta).get("schema")
92
+ res = self.session.scalars(
93
+ select(Meta).where(Meta.key == "schema")).first()
94
94
  if res:
95
95
  return int(res.value)
96
96
 
@@ -121,11 +121,10 @@ class Database:
121
121
  # simply drop and re-create the tables
122
122
  self.logger.debug("Upgrading to schema version 2")
123
123
 
124
- table = Table("episodes", self.metadata, autoload=True)
125
- table.drop()
126
-
127
- table = Table("shows", self.metadata, autoload=True)
128
- table.drop()
124
+ upgrade = sqlite3.connect(self._path)
125
+ upgrade.execute("DROP TABLE episodes")
126
+ upgrade.execute("DROP TABLE shows")
127
+ upgrade.close()
129
128
 
130
129
  Show.__table__.create(bind=self.engine)
131
130
  Episode.__table__.create(bind=self.engine)
@@ -146,9 +145,9 @@ class Database:
146
145
 
147
146
  upgrade = sqlite3.connect(self._path)
148
147
  upgrade.execute("ALTER TABLE shows "
149
- "ADD COLUMN enabled TYPE boolean")
148
+ "ADD COLUMN enabled TYPE boolean")
150
149
  upgrade.execute("ALTER TABLE shows "
151
- "ADD COLUMN status TYPE integer")
150
+ "ADD COLUMN status TYPE integer")
152
151
  upgrade.close()
153
152
 
154
153
  self.open()
@@ -167,7 +166,7 @@ class Database:
167
166
 
168
167
  upgrade = sqlite3.connect(self._path)
169
168
  upgrade.execute("ALTER TABLE episodes "
170
- "ADD COLUMN notified TYPE date")
169
+ "ADD COLUMN notified TYPE date")
171
170
  upgrade.close()
172
171
 
173
172
  self.open()
@@ -216,7 +215,8 @@ class Database:
216
215
 
217
216
  def get_show_by_id(self, show_id):
218
217
 
219
- return self.session.query(Show).get(show_id)
218
+ return self.session.scalars(
219
+ select(Show).where(Show.id == show_id)).first()
220
220
 
221
221
  def add_show(self, show):
222
222
 
@@ -226,15 +226,17 @@ class Database:
226
226
 
227
227
  def remove_show(self, show_id):
228
228
 
229
- show = self.session.query(Show).get(show_id)
229
+ show = self.session.scalars(
230
+ select(Show).where(Show.id == show_id)).first()
230
231
 
231
232
  if not show:
232
233
  self.logger.error("No such show")
233
234
  return
234
235
 
235
- episodes = self.session.query(Episode)
236
+ episodes = self.session.scalars(select(Episode))
236
237
 
237
- for episode in episodes.filter(Episode.show_id == show.id):
238
+ hits = [e for e in episodes if e.show_id == show.id]
239
+ for episode in hits:
238
240
  self.session.delete(episode)
239
241
 
240
242
  self.session.delete(show)
@@ -254,18 +256,18 @@ class Database:
254
256
 
255
257
  enddate = basedate + timedelta(n_days)
256
258
 
257
- return self.session.query(Episode).\
259
+ return self.session.query(Episode). \
258
260
  filter(Episode.airdate >= basedate). \
259
261
  filter(Episode.airdate <= enddate). \
260
262
  order_by(Episode.airdate).all()
261
263
 
262
264
  def search(self, search):
263
265
 
264
- return self.session.query(Episode).\
265
- filter(or_( \
266
- Episode.title.like("%%%s%%" % search),
267
- Show.name.like("%%%s%%" % search))). \
268
- order_by(Episode.airdate).all()
266
+ q = self.session.query(Episode).join(Episode.show)
267
+ return q.filter(or_(
268
+ Episode.title.like(f"%%{search}%%"),
269
+ Show.name.like(f"%%{search}%%"))). \
270
+ order_by(Episode.airdate.asc()).all()
269
271
 
270
272
  def commit(self):
271
273