episoder 0.10.1__tar.gz → 0.11.0__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 (78) hide show
  1. episoder-0.11.0/.gitignore +2 -0
  2. episoder-0.11.0/.gitlab-ci.yml +107 -0
  3. episoder-0.11.0/CHANGELOG +219 -0
  4. {episoder-0.10.1 → episoder-0.11.0}/PKG-INFO +5 -1
  5. {episoder-0.10.1 → episoder-0.11.0}/episoder/database.py +19 -18
  6. {episoder-0.10.1 → episoder-0.11.0}/episoder/episoder.py +38 -36
  7. {episoder-0.10.1 → episoder-0.11.0}/episoder/main.py +59 -55
  8. {episoder-0.10.1 → episoder-0.11.0}/episoder/output.py +12 -13
  9. {episoder-0.10.1 → episoder-0.11.0}/episoder/sources/__init__.py +1 -1
  10. {episoder-0.10.1 → episoder-0.11.0}/episoder/sources/epguides.py +1 -1
  11. {episoder-0.10.1 → episoder-0.11.0}/episoder/sources/parser.py +1 -1
  12. {episoder-0.10.1 → episoder-0.11.0}/episoder/sources/thetvdb.py +3 -3
  13. {episoder-0.10.1 → episoder-0.11.0}/episoder/sources/tvcom.py +1 -1
  14. {episoder-0.10.1 → episoder-0.11.0}/episoder/version.py +2 -2
  15. episoder-0.11.0/episoder.1 +247 -0
  16. {episoder-0.10.1 → episoder-0.11.0}/episoder.egg-info/PKG-INFO +5 -1
  17. episoder-0.11.0/episoder.egg-info/SOURCES.txt +76 -0
  18. {episoder-0.10.1 → episoder-0.11.0}/episoder.egg-info/requires.txt +1 -1
  19. episoder-0.11.0/examples/home.episoder +30 -0
  20. {episoder-0.10.1 → episoder-0.11.0}/setup.cfg +1 -1
  21. episoder-0.11.0/test/__init__.py +0 -0
  22. episoder-0.11.0/test/fixtures/epguides_48_hours_mistery.html +399 -0
  23. episoder-0.11.0/test/fixtures/epguides_american_idol.html +872 -0
  24. episoder-0.11.0/test/fixtures/epguides_bsg.html +538 -0
  25. episoder-0.11.0/test/fixtures/epguides_buzzcocks.html +464 -0
  26. episoder-0.11.0/test/fixtures/epguides_doctor_who.html +842 -0
  27. episoder-0.11.0/test/fixtures/epguides_eureka.html +362 -0
  28. episoder-0.11.0/test/fixtures/epguides_futurama.html +407 -0
  29. episoder-0.11.0/test/fixtures/epguides_house.html +490 -0
  30. episoder-0.11.0/test/fixtures/epguides_kr2008.html +256 -0
  31. episoder-0.11.0/test/fixtures/epguides_lost.html +478 -0
  32. episoder-0.11.0/test/fixtures/epguides_lost_html.html +602 -0
  33. episoder-0.11.0/test/fixtures/epguides_lower_decks.html +340 -0
  34. episoder-0.11.0/test/fixtures/epguides_midsomer_murders.html +398 -0
  35. episoder-0.11.0/test/fixtures/epguides_picard.html +380 -0
  36. episoder-0.11.0/test/fixtures/epguides_test_iso_8859_1.html +138 -0
  37. episoder-0.11.0/test/fixtures/tvdb_error_404.json +3 -0
  38. episoder-0.11.0/test/fixtures/tvdb_search_frasier.json +10 -0
  39. episoder-0.11.0/test/fixtures/tvdb_search_friends.json +28 -0
  40. episoder-0.11.0/test/fixtures/tvdb_test_show_260.json +1 -0
  41. episoder-0.11.0/test/fixtures/tvdb_test_show_260_1_eps.json +19 -0
  42. episoder-0.11.0/test/fixtures/tvdb_test_show_261.json +1 -0
  43. episoder-0.11.0/test/fixtures/tvdb_test_show_261_1_eps.json +23 -0
  44. episoder-0.11.0/test/fixtures/tvdb_test_show_261_2_eps.json +23 -0
  45. episoder-0.11.0/test/fixtures/tvdb_test_show_261_3_eps.json +18 -0
  46. episoder-0.11.0/test/fixtures/tvdb_test_show_262.json +1 -0
  47. episoder-0.11.0/test/fixtures/tvdb_test_show_262_1_eps.json +20 -0
  48. episoder-0.11.0/test/fixtures/tvdb_test_show_263.json +1 -0
  49. episoder-0.11.0/test/fixtures/tvdb_test_show_268156.json +1 -0
  50. episoder-0.11.0/test/fixtures/tvdb_test_show_268156_1_eps.json +520 -0
  51. episoder-0.11.0/test/fixtures/tvdb_test_show_295640.json +1 -0
  52. episoder-0.11.0/test/fixtures/tvdb_test_show_295640_1_eps.json +1 -0
  53. episoder-0.11.0/test/fixtures/tvdb_test_show_295640_2_eps.json +1 -0
  54. episoder-0.11.0/test/fixtures/tvdb_test_show_73739.json +1 -0
  55. episoder-0.11.0/test/fixtures/tvdb_test_show_73739_1_eps.json +14 -0
  56. episoder-0.11.0/test/fixtures/tvdb_test_show_75397.json +1 -0
  57. episoder-0.11.0/test/fixtures/tvdb_test_show_75397_1_eps.json +1 -0
  58. {episoder-0.10.1 → episoder-0.11.0}/test/test_database.py +68 -68
  59. {episoder-0.10.1 → episoder-0.11.0}/test/test_epguides_parser.py +27 -23
  60. {episoder-0.10.1 → episoder-0.11.0}/test/test_episode.py +15 -15
  61. {episoder-0.10.1 → episoder-0.11.0}/test/test_metainfo.py +1 -1
  62. {episoder-0.10.1 → episoder-0.11.0}/test/test_migration.py +113 -125
  63. {episoder-0.10.1 → episoder-0.11.0}/test/test_notify.py +53 -41
  64. {episoder-0.10.1 → episoder-0.11.0}/test/test_output.py +30 -30
  65. {episoder-0.10.1 → episoder-0.11.0}/test/test_show.py +14 -14
  66. {episoder-0.10.1 → episoder-0.11.0}/test/test_sources.py +4 -1
  67. {episoder-0.10.1 → episoder-0.11.0}/test/test_tvcom.py +2 -2
  68. {episoder-0.10.1 → episoder-0.11.0}/test/test_tvdb.py +29 -28
  69. episoder-0.11.0/test/util.py +60 -0
  70. episoder-0.10.1/episoder.egg-info/SOURCES.txt +0 -33
  71. {episoder-0.10.1 → episoder-0.11.0}/COPYING +0 -0
  72. {episoder-0.10.1 → episoder-0.11.0}/README.md +0 -0
  73. {episoder-0.10.1 → episoder-0.11.0}/episoder/__init__.py +0 -0
  74. {episoder-0.10.1 → episoder-0.11.0}/episoder.egg-info/dependency_links.txt +0 -0
  75. {episoder-0.10.1 → episoder-0.11.0}/episoder.egg-info/entry_points.txt +0 -0
  76. {episoder-0.10.1 → episoder-0.11.0}/episoder.egg-info/top_level.txt +0 -0
  77. {episoder-0.10.1 → episoder-0.11.0}/pyproject.toml +0 -0
  78. {episoder-0.10.1 → episoder-0.11.0}/setup.py +0 -0
@@ -0,0 +1,2 @@
1
+ *.pyc
2
+ *.swp
@@ -0,0 +1,107 @@
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
+ .noble_image: &noble
12
+ image: registry.gitlab.com/schabe/episoder:ubuntu-noble
13
+
14
+ .test_template: &test_installed_setuptools
15
+ stage: test
16
+ script:
17
+ - python3 setup.py install
18
+ - python3 -m pytest --junitxml=report.xml --cov=episoder/
19
+ - python3 -m coverage xml
20
+ artifacts:
21
+ reports:
22
+ junit: report.xml
23
+ coverage_report:
24
+ coverage_format: cobertura
25
+ path: coverage.xml
26
+
27
+ .test_template: &test
28
+ stage: test
29
+ script:
30
+ - python3 -m pytest --junitxml=report.xml --cov=episoder/
31
+ - python3 -m coverage xml
32
+ artifacts:
33
+ reports:
34
+ junit: report.xml
35
+ coverage_report:
36
+ coverage_format: cobertura
37
+ path: coverage.xml
38
+
39
+ .deploy_setuptools_template: &deploy_setuptools
40
+ stage: deploy
41
+ script:
42
+ - python3 setup.py install
43
+ - python3 setup.py sdist
44
+ - python3 setup.py bdist_egg
45
+ - episoder --version
46
+
47
+ .deploy_pip_template: &deploy_pip
48
+ stage: deploy
49
+ script:
50
+ - pip install .
51
+ - episoder --version
52
+
53
+ .deploy_setuptools_venv_template: &deploy_pip_venv
54
+ stage: deploy
55
+ script:
56
+ - python3 -m venv --system-site-packages venv
57
+ - venv/bin/python3 setup.py install
58
+ - venv/bin/python3 setup.py sdist
59
+ - venv/bin/python3 setup.py bdist_egg
60
+ - venv/bin/episoder --version
61
+
62
+ .deploy_pip_venv_template: &deploy_pip_venv
63
+ stage: deploy
64
+ script:
65
+ - python3 -m venv --system-site-packages venv
66
+ - venv/bin/pip install .
67
+ - venv/bin/episoder --version
68
+
69
+ py310-jammy:test:
70
+ <<: *jammy
71
+ <<: *test
72
+
73
+ py310-jammy-setuptools:deploy:
74
+ <<: *jammy
75
+ <<: *deploy_setuptools
76
+
77
+ py310-jammy-pip:deploy:
78
+ <<: *jammy
79
+ <<: *deploy_pip
80
+
81
+ py311-bookworm:test:
82
+ <<: *bookworm
83
+ <<: *test
84
+
85
+ py311-bookworm-setuptools:deploy:
86
+ needs: ["py311-bookworm:test"]
87
+ <<: *bookworm
88
+ <<: *deploy_setuptools
89
+
90
+ py311-bookworm-pip:deploy:
91
+ needs: ["py311-bookworm:test"]
92
+ <<: *bookworm
93
+ <<: *deploy_pip_venv
94
+
95
+ py312-noble:test:
96
+ <<: *noble
97
+ <<: *test
98
+
99
+ py312-noble-setuptools:deploy:
100
+ needs: ["py312-noble:test"]
101
+ <<: *noble
102
+ <<: *deploy_setuptools
103
+
104
+ py312-noble-pip:deploy:
105
+ needs: ["py312-noble:test"]
106
+ <<: *noble
107
+ <<: *deploy_pip_venv
@@ -0,0 +1,219 @@
1
+ == 0.11.0 (20 Jul 2024) ==
2
+ * Port to sqlalchemy 2.0
3
+
4
+ == 0.10.1 (17 May 2023) ==
5
+ * tvdb: Fix a crash on shows with an empty 'data' field
6
+ * epguides: Fix parsing of cancelled/ended shows
7
+
8
+ == 0.10.0 (25 Mar 2023) ==
9
+ * Fix a crash when trying to render shows without a name
10
+ * Explicitly depend on sqlalchemy 1.3/1.4
11
+ * Fix code that sets the database schema version
12
+ * Rename package from pyepisoder to episoder
13
+ * Update distribution scripts
14
+
15
+ == 0.9.2 (1 Jan 2021) ==
16
+ * Fix a crash when trying to render episodes without production number
17
+ * Use appropriate colors when showing episodes from a past date
18
+ * Fix formatting of show names in list of shows
19
+ * Accept empty episode lines in epguide HTML format parser
20
+
21
+ == 0.9.1 (11 Oct 2020) ==
22
+ * Fix setup.py
23
+
24
+ == 0.9.0 (11 Oct 2020) ==
25
+ * Add support for new HTML format on epguides.com (thanks to Jethro Beekman)
26
+
27
+ == 0.8.5 (28 Mar 2020) ==
28
+ * Fix a small bug in setup.py
29
+
30
+ == 0.8.4 (28 Mar 2020) ==
31
+ * Fix loading of shows without episodes from TVDB
32
+ * Fix loading of episodes with invalid date (0000-00-00)
33
+
34
+ == 0.8.3 (27 Sep 2017) ==
35
+ * Fixed a crash related to unexpected null values in TVDB JSON data
36
+ * Use correct path for example config file in setup.py (fixes #7)
37
+
38
+ == 0.8.2 (7 Jul 2017) ==
39
+ * Fixed a crash when rendering episodes without a title
40
+
41
+ == 0.8.1 (24 Jun 2017) ==
42
+ * Handle failures to load TVDB API pages
43
+
44
+ == 0.8.0 (7 Jun 2017) ==
45
+ * Switched from distutils to setuptools
46
+ * We now use the new TVDB JSON API
47
+ * Rewrote the epguides.com parser in pure Python
48
+ * Rewrote large parts of the database code
49
+ * Episoder should now work fully with Python 3
50
+ * The update command can now be limited to a fixed number of shows
51
+ * Updated man page
52
+ * Thanks to Chris Spencer (chrisspen) for his contributions
53
+ * Added option to only list active shows (chrisspen)
54
+ * Added the number of episodes to the list of shows (chrisspen)
55
+ * Added email notification feature via 'episoder notify' (chrisspen)
56
+
57
+ == 0.7.3 (7 Nov 2015) ==
58
+ * Fixed a syntax error in the list of shows
59
+ * Fixed season number parsing for epguides.com (thanks to jethrogb)
60
+
61
+ == 0.7.2 (26 Sep 2015) ==
62
+ * Dropped unneeded dependency on BeautifulSoup
63
+ * Fixed episode number parsing for epguides.com (thanks to jethrogb)
64
+ * Improved python3 compatibility
65
+
66
+ == 0.7.1 (27 Apr 2014) ==
67
+ * Accept unicode characters in prodnum fields
68
+
69
+ == 0.7.0 (14 Oct 2013) ==
70
+ * Added support for TheTVDB.com
71
+ * Removed support for tv.com
72
+ * Changed command-line arguments. See the man page for details
73
+ * Removed the ability to specify shows in the configuration file
74
+ * Reorganized the command line options. Check the documentation for details
75
+ * Updated the epguides.com parser to ignore trailer links in episode names
76
+ * Fixed some more encoding issues in the console renderer
77
+ * Changed license to GPLv3
78
+
79
+ == 0.6.5 (6 Oct 2010) ==
80
+ * Updated the epguides.com parser to ignore links to trailers in episode names
81
+
82
+ == 0.6.4 (25 Jun 2010) ==
83
+ * Filter javascript from tv.com pages because it confuses beautifulsoup 3.1
84
+
85
+ == 0.6.3 (23 Jun 2010) ==
86
+ * The epguides parser now properly reads the season number for more shows
87
+
88
+ == 0.6.2 (08 Nov 2009) ==
89
+ * Updated tv.com parser (show names need to be lower-case now!)
90
+ * Added -S option to specify a show id for (forced) updates
91
+
92
+ == 0.6.1 (26 Aug 2009) ==
93
+ * Episoder can now talk to any database supported by sqlalchemy (see README)
94
+ * Tell awk to process utf8 (for epguides parser)
95
+ * Catch (and properly log) some more HTTP exceptions
96
+
97
+ == 0.6.0 (8 Aug 2009) ==
98
+ * Episoder now keeps a persistent database of shows and episodes
99
+ * Added new options (-L, -a, -r, -E, -D) to manage the database
100
+ * The parsers now know whether a show is still running or not
101
+ * Active (currently running) shows are updated every two days
102
+ * Inactive (ended) shows are updated every two weeks
103
+ * The new -B command overrides those intervals and forces an update
104
+ * Show names can no longer be overridden in the configuration file
105
+ * Don't report an error when no sources are defined in the config file
106
+ * The tv.com parser now also accepts urls containing '!', '+' or '%'
107
+ * The -d option now also accepts the number of days instead of a fixed date
108
+ * Documentation updated
109
+
110
+ == 0.5.3 (26 Jul 2009) ==
111
+ * Added support for another epguides formatting style (used eg. for "Eureka")
112
+ * Added option to suppress colors in console output
113
+ * Added a filter to remove invalid characters from epguides content
114
+ * Fixed some other parsing issues in the epguides parser
115
+ * Improved compatibility with older python versions
116
+
117
+ == 0.5.2 (30 Jun 2009) ==
118
+ * Reworked parts of the code to improve compatibility with older libraries
119
+ * Fixed some encoding issues in the console renderer
120
+
121
+ == 0.5.1 (23 Jun 2009) ==
122
+ * Thanks to Philip Douglass for his bug reports and patches
123
+ * The epguides parser now properly closes all file handles and works with mawk
124
+ * The tv.com parser would crash on missing production/season/episode numbers
125
+ * The tv.com parser now accepts urls that contain a dash
126
+ * Switched to using sqlalchemy to access the database
127
+ * Properly implemented the user-agent configuration option
128
+ * Removed old FAQ
129
+ * Fixed some text encoding issues
130
+ * Small fixes to the man page
131
+
132
+ == 0.5.0 (18 May 2009) ==
133
+ * Rewrote the whole thing in python
134
+ * Wrote a brand new tv.com parser
135
+ * Small fixes in the epguides.com parser
136
+ * Parsed data is now stored in an sqlite3 database
137
+
138
+ == 0.4.7 (24 Feb 2007) ==
139
+ * Fixed a parsing issue introduced with 0.4.6
140
+
141
+ == 0.4.6 (24 Feb 2007) ==
142
+ * Discovered yet another formatting possibility on epguides.com (used for
143
+ instance for the show "Midsomer Murders") and adapted the parser accordingly
144
+ * Slightly updated the man page
145
+
146
+ == 0.4.5 (23 Jan 2007) ==
147
+ * Fixed some epguides parser issues
148
+
149
+ == 0.4.4 (19 Nov 2006) ==
150
+ * Fixed the -i option when building the database (did the exact opposite of
151
+ what it was supposed to do)
152
+
153
+ == 0.4.3 (24 Oct 2006) ==
154
+ * We now actually honor the -i option when building the database
155
+ * 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/
156
+ * Oops, forgot to update episoder's version number in the last release...
157
+ * Fixed a bug in the new epguides.com parser which would not drop episodes without a date
158
+
159
+ == 0.4.2 (21 Oct 2006) ==
160
+ * Don't ignore the config file passed with -c anymore when building the db
161
+ * Added parser for epguides.com (closes #38)
162
+
163
+ == 0.4.1 (20 Apr 2006) ==
164
+ * Did some minor internal changes to get episoder to work on non-linux systems
165
+ * Removed long options
166
+ * Hotfix for the tv.com website changes
167
+
168
+ == 0.4.0 (3 Dec 2005) ==
169
+ * 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)
170
+ * Search is now alway case-insensitive
171
+ * If no action is specified, episoder now defaults to showlist
172
+ * Removed the tvtome dummy plugin
173
+
174
+ == 0.3.0 (9 Aug 2005) ==
175
+ * Majeed Goldrick joined the project
176
+ * Moved parts of the website to sourceforge
177
+ * Removed a 1024-episodes-limit in sortlist
178
+ * Added search options
179
+ * Switched to awk to remove old episodes (>600 times faster)
180
+ * Switched to awk for tv.com parsing (>10 times faster)
181
+ * Removed the debian files from the main distribution
182
+ * Added a couple of tests
183
+
184
+ == 0.2.5 (19 Jul 2005) ==
185
+ * Fixed an alignment problem in showlist (closes #25)
186
+ * Fixed a date-calculation issue in showlist (closes #23)
187
+ * Fixed a documentation mistake about the format string (closes #21)
188
+ * Added support for nun-numeric episode numbers (closes #24)
189
+
190
+ == 0.2.4 (19 Jul 2005) ==
191
+ * Re-arranged the code to remove a 3-episodes-limit in showlist (closes #19)
192
+ * Added command line option to specify the number of days to show (closes #20)
193
+ * Brought back season-episode information in the tvcom plugin (closes #15)
194
+ * Added the possibility to customize the output format (closes #16)
195
+ * Added a small FAQ
196
+ * Thanks to Majeed Goldrick for all the bug reports and feature suggestions
197
+ * Cosmetic changes
198
+
199
+ == 0.2.3 (23 Jun 2005) ==
200
+ * The tvcom plugin now uses the new "All Seasons" link
201
+ * Cosmetic changes
202
+
203
+ == 0.2.2 (17 Jun 2005) ==
204
+ * Added LICENSE file
205
+ * install.sh now correctly changes the EPISODER_HOME value
206
+ * Small fixes in the verbose output
207
+ * Created tvcom plugin
208
+ * Replaced all functionality from the tvtome plugin with a warning
209
+ * Now all the available modules are actually loaded
210
+ * Some minor changes in the man page
211
+
212
+ == 0.2.1 (4 Jan 2005) ==
213
+ * The tvtome plugin now understands UNAIRED episodes
214
+ * Improved the builddb script's debug output
215
+ * Fixed an issue that would print errors for shows with no upcoming episodes
216
+ * The debian files are now part of the main distribution
217
+
218
+ == 0.2.0 (13 Sep 2004) ==
219
+ * Initial public release
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: episoder
3
- Version: 0.10.1
3
+ Version: 0.11.0
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
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