🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    .github/workflows/image-javascript.yml
    skipped 19 lines
    20 20   python -m pip install --upgrade pip
    21 21   pip install flake8 pytest
    22 22   if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
     23 + if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
    23 24   
    24 25   - name: Lint with flake8
    25 26   run: |
    skipped 4 lines
    30 31   
    31 32   - name: Create release metadata
    32 33   run: |
    33  - # COPY'ed by Dockerfile into backend/ of the image, then read by the server in store.py
    34  - echo ${{ github.sha }} > backend/source.txt
    35  - echo ${{ github.ref }} > backend/tag.txt
     34 + # COPY'ed by Dockerfile into changedetectionio/ of the image, then read by the server in store.py
     35 + echo ${{ github.sha }} > changedetectionio/source.txt
     36 + echo ${{ github.ref }} > changedetectionio/tag.txt
    36 37   
    37 38   - name: Test with pytest
    38 39   run: |
    39 40   # Each test is totally isolated and performs its own cleanup/reset
    40  - cd backend; ./run_all_tests.sh
     41 + cd changedetectionio; ./run_all_tests.sh
    41 42   
    42 43   - name: Set up QEMU
    43 44   uses: docker/setup-qemu-action@v1
    skipped 45 lines
  • ■ ■ ■ ■ ■ ■
    .github/workflows/image-tag.yml
    skipped 28 lines
    29 29   python -m pip install --upgrade pip
    30 30   pip install flake8 pytest
    31 31   if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
     32 + if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
    32 33   
    33 34   - name: Lint with flake8
    34 35   run: |
    skipped 4 lines
    39 40   
    40 41   - name: Create release metadata
    41 42   run: |
    42  - # COPY'ed by Dockerfile into backend/ of the image, then read by the server in store.py
    43  - echo ${{ github.sha }} > backend/source.txt
    44  - echo ${{ github.ref }} > backend/tag.txt
     43 + # COPY'ed by Dockerfile into changedetectionio/ of the image, then read by the server in store.py
     44 + echo ${{ github.sha }} > changedetectionio/source.txt
     45 + echo ${{ github.ref }} > changedetectionio/tag.txt
    45 46   
    46 47   - name: Test with pytest
    47 48   run: |
    48 49   # Each test is totally isolated and performs its own cleanup/reset
    49  - cd backend; ./run_all_tests.sh
     50 + cd changedetectionio; ./run_all_tests.sh
    50 51   
    51 52   - name: Set up QEMU
    52 53   uses: docker/setup-qemu-action@v1
    skipped 40 lines
  • ■ ■ ■ ■ ■ ■
    .github/workflows/image.yml
    skipped 19 lines
    20 20   python -m pip install --upgrade pip
    21 21   pip install flake8 pytest
    22 22   if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
     23 + if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
    23 24   
    24 25   - name: Lint with flake8
    25 26   run: |
    skipped 4 lines
    30 31   
    31 32   - name: Create release metadata
    32 33   run: |
    33  - # COPY'ed by Dockerfile into backend/ of the image, then read by the server in store.py
    34  - echo ${{ github.sha }} > backend/source.txt
    35  - echo ${{ github.ref }} > backend/tag.txt
     34 + # COPY'ed by Dockerfile into changedetectionio/ of the image, then read by the server in store.py
     35 + echo ${{ github.sha }} > changedetectionio/source.txt
     36 + echo ${{ github.ref }} > changedetectionio/tag.txt
    36 37   
    37 38   - name: Test with pytest
    38 39   run: |
    39 40   # Each test is totally isolated and performs its own cleanup/reset
    40  - cd backend; ./run_all_tests.sh
     41 + cd changedetectionio; ./run_all_tests.sh
    41 42   
    42 43   - name: Set up QEMU
    43 44   uses: docker/setup-qemu-action@v1
    skipped 45 lines
  • ■ ■ ■ ■ ■
    .github/workflows/test-only.yml
    skipped 18 lines
    19 19   python -m pip install --upgrade pip
    20 20   pip install flake8 pytest
    21 21   if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
    22  - 
     22 + if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
    23 23   - name: Lint with flake8
    24 24   run: |
    25 25   # stop the build if there are Python syntax errors or undefined names
    26 26   flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
    27 27   # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
    28 28   flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
     29 + 
    29 30   - name: Test with pytest
    30 31   run: |
    31 32   # Each test is totally isolated and performs its own cleanup/reset
    32  - cd backend; ./run_all_tests.sh
     33 + cd changedetectionio; ./run_all_tests.sh
     34 + 
     35 + - name: Test that pip builds without error
     36 + run: |
     37 + pip3 --version
     38 + python3 -m pip install wheel
     39 + python3 setup.py bdist_wheel
     40 + python3 -m pip install dist/changedetection.io-*-none-any.whl --force
     41 + changedetection.io -d /tmp -p 10000 &
     42 + sleep 3
     43 + curl http://127.0.0.1:10000/static/styles/pure-min.css >/dev/null
     44 + killall -9 changedetection.io
     45 + 
    33 46   
    34 47   
  • ■ ■ ■ ■ ■ ■
    .gitignore
    skipped 4 lines
    5 5  datastore/*
    6 6  __pycache__
    7 7  .pytest_cache
     8 +build
     9 +dist
    8 10   
  • ■ ■ ■ ■
    Dockerfile
    skipped 46 lines
    47 47  ENV PYTHONPATH=/usr/local
    48 48   
    49 49  # The actual flask app
    50  -COPY backend /app/backend
     50 +COPY changedetectionio /app/changedetectionio
    51 51  # The eventlet server wrapper
    52 52  COPY changedetection.py /app/changedetection.py
    53 53   
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    MANIFEST.in
     1 +recursive-include changedetectionio/templates *
     2 +recursive-include changedetectionio/static *
     3 +include changedetection.py
     4 +global-exclude *.pyc
  • ■ ■ ■ ■ ■ ■
    README-pip.md
     1 +# changedetection.io
     2 +![changedetection.io](https://github.com/dgtlmoon/changedetection.io/actions/workflows/test-only.yml/badge.svg?branch=master)
     3 +<a href="https://hub.docker.com/r/dgtlmoon/changedetection.io" target="_blank" title="Change detection docker hub">
     4 + <img src="https://img.shields.io/docker/pulls/dgtlmoon/changedetection.io" alt="Docker Pulls"/>
     5 +</a>
     6 +<a href="https://hub.docker.com/r/dgtlmoon/changedetection.io" target="_blank" title="Change detection docker hub">
     7 + <img src="https://img.shields.io/github/v/release/dgtlmoon/changedetection.io" alt="Change detection latest tag version"/>
     8 +</a>
     9 + 
     10 +## Self-hosted open source change monitoring of web pages.
     11 + 
     12 +_Know when web pages change! Stay ontop of new information!_
     13 + 
     14 +Live your data-life *pro-actively* instead of *re-actively*, do not rely on manipulative social media for consuming important information.
     15 + 
     16 + 
     17 +<img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/screenshot.png" style="max-width:100%;" alt="Self-hosted web page change monitoring" title="Self-hosted web page change monitoring" />
     18 + 
     19 +#### Example use cases
     20 + 
     21 +Know when ...
     22 + 
     23 +- Government department updates (changes are often only on their websites)
     24 +- Local government news (changes are often only on their websites)
     25 +- New software releases, security advisories when you're not on their mailing list.
     26 +- Festivals with changes
     27 +- Realestate listing changes
     28 +- COVID related news from government websites
     29 +- Detect and monitor changes in JSON API responses
     30 +- API monitoring and alerting
     31 + 
     32 +**Get monitoring now!**
     33 + 
     34 +```bash
     35 +$ pip3 install changedetection.io
     36 +```
     37 + 
     38 +Specify a target for the *datastore path* with `-d` (required) and a *listening port* with `-p` (defaults to `5000`)
     39 + 
     40 +```bash
     41 +$ changedetection.io -d /path/to/empty/data/dir -p 5000
     42 +```
     43 + 
     44 + 
     45 +Then visit http://127.0.0.1:5000 , You should now be able to access the UI.
     46 + 
     47 +### Features
     48 +- Website monitoring
     49 +- Change detection of content and analyses
     50 +- Filters on change (Select by CSS or JSON)
     51 +- Triggers (Wait for text, wait for regex)
     52 +- Notification support
     53 +- JSON API Monitoring
     54 +- Parse JSON embedded in HTML
     55 +- (Reverse) Proxy support
     56 +- Javascript support via WebDriver
     57 +- RaspberriPi (arm v6/v7/64 support)
     58 + 
     59 +See https://github.com/dgtlmoon/changedetection.io for more information.
     60 + 
     61 + 
     62 + 
     63 +### Support us
     64 + 
     65 +Do you use changedetection.io to make money? does it save you time or money? Does it make your life easier? less stressful? Remember, we write this software when we should be doing actual paid work, we have to buy food and pay rent just like you.
     66 + 
     67 +Please support us, even small amounts help a LOT.
     68 + 
     69 +BTC `1PLFN327GyUarpJd7nVe7Reqg9qHx5frNn`
     70 + 
     71 +<img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/btc-support.png" style="max-width:50%;" alt="Support us!" />
     72 + 
  • ■ ■ ■ ■ ■ ■
    README.md
    skipped 12 lines
    13 13   
    14 14  Live your data-life *pro-actively* instead of *re-actively*, do not rely on manipulative social media for consuming important information.
    15 15   
     16 +Open source web page monitoring, notification and change detection.
     17 + 
    16 18   
    17 19  <img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/screenshot.png" style="max-width:100%;" alt="Self-hosted web page change monitoring" title="Self-hosted web page change monitoring" />
    18 20   
    skipped 10 lines
    29 31  - Detect and monitor changes in JSON API responses
    30 32  - API monitoring and alerting
    31 33   
    32  -_Need an actual Chrome runner with Javascript support? see the experimental <a href="https://github.com/dgtlmoon/changedetection.io/tree/javascript-browser">Javascript/Chrome support changedetection.io branch!</a>_
     34 +_Need an actual Chrome runner with Javascript support? We support fetching via WebDriver!</a>_
    33 35   
    34 36  **Get monitoring now! super simple, one command!**
    35 37   
    36 38  Run the python code on your own machine by cloning this repository, or with <a href="https://docs.docker.com/get-docker/">docker</a> and/or <a href="https://www.digitalocean.com/community/tutorial_collections/how-to-install-docker-compose">docker-compose</a>
    37 39   
    38  -With one docker-compose command
     40 +**Docker**
    39 41   
     42 +With Docker composer, just clone this repository and
    40 43  ```bash
    41  -docker-compose up -d
     44 +$ docker-compose up -d
     45 +```
     46 +Docker standalone
     47 +```bash
     48 +$ docker run -d --restart always -p "127.0.0.1:5000:5000" -v datastore-volume:/datastore --name changedetection.io dgtlmoon/changedetection.io
     49 +```
     50 + 
     51 +**Python PIP**
     52 +```bash
     53 +$ pip3 install changedetection.io
     54 +$ changedetection.io -d /path/to/empty/data/dir -p 5000
    42 55  ```
    43 56   
    44 57  Then visit http://127.0.0.1:5000 , You should now be able to access the UI.
    45 58   
    46 59  _Now with per-site configurable support for using a fast built in HTTP fetcher or use a Chrome based fetcher for monitoring of JavaScript websites!_
    47 60   
    48  - 
    49  -#### Updating to the latest version
    50  - 
    51  -Highly recommended :)
    52  - 
    53  -```bash
    54  -docker pull dgtlmoon/changedetection.io
    55  -docker-compose up -d
    56  -```
    57 61  
    58 62  ### Screenshots
    59 63   
    skipped 69 lines
    129 133  For more information see https://docs.python-requests.org/en/master/user/advanced/#proxies
    130 134   
    131 135  This proxy support also extends to the notifications https://github.com/caronc/apprise/issues/387#issuecomment-841718867
    132  - 
    133  - 
    134  -### Notes
    135  - 
    136  -- ~~Does not yet support Javascript~~
    137  -- ~~Wont work with Cloudfare type "Please turn on javascript" protected pages~~
    138  -- You can use the 'headers' section to monitor password protected web page changes
    139  - 
    140  -See the experimental <a href="https://github.com/dgtlmoon/changedetection.io/tree/javascript-browser">Javascript/Chrome browser support!</a>
    141 136   
    142 137   
    143 138  ### RaspberriPi support?
    skipped 14 lines
  • ■ ■ ■ ■ ■ ■
    changedetection.py
    skipped 7 lines
    8 8   
    9 9  import eventlet
    10 10  import eventlet.wsgi
    11  -import backend
     11 +import changedetectionio
    12 12   
    13  -from backend import store
     13 +from changedetectionio import store
    14 14   
    15  -def main(argv):
     15 +def main():
    16 16   ssl_mode = False
    17 17   port = os.environ.get('PORT') or 5000
    18 18   do_cleanup = False
    skipped 2 lines
    21 21   datastore_path = os.path.join(os.getcwd(), "datastore")
    22 22   
    23 23   try:
    24  - opts, args = getopt.getopt(argv, "csd:p:", "port")
     24 + opts, args = getopt.getopt(sys.argv[1:], "csd:p:", "port")
    25 25   except getopt.GetoptError:
    26 26   print('backend.py -s SSL enable -p [port] -d [datastore path]')
    27 27   sys.exit(2)
    skipped 20 lines
    48 48   # isnt there some @thingy to attach to each route to tell it, that this route needs a datastore
    49 49   app_config = {'datastore_path': datastore_path}
    50 50   
    51  - datastore = store.ChangeDetectionStore(datastore_path=app_config['datastore_path'])
    52  - app = backend.changedetection_app(app_config, datastore)
     51 + if not os.path.isdir(app_config['datastore_path']):
     52 + print ("ERROR: Directory path for the datastore '{}' does not exist, cannot start, please make sure the directory exists.\n"
     53 + "Alternatively, use the -d parameter.".format(app_config['datastore_path']),file=sys.stderr)
     54 + sys.exit(2)
     55 + 
     56 + datastore = store.ChangeDetectionStore(datastore_path=app_config['datastore_path'], version_tag=changedetectionio.__version__)
     57 + app = changedetectionio.changedetection_app(app_config, datastore)
    53 58   
    54 59   # Go into cleanup mode
    55 60   if do_cleanup:
    skipped 33 lines
    89 94   
    90 95   
    91 96  if __name__ == '__main__':
    92  - main(sys.argv[1:])
     97 + main()
    93 98   
  • ■ ■ ■ ■ ■ ■
    backend/__init__.py changedetectionio/__init__.py
    skipped 28 lines
    29 29  import datetime
    30 30  import pytz
    31 31   
     32 +__version__ = '0.39'
     33 + 
    32 34  datastore = None
    33 35   
    34 36  # Local
    skipped 6 lines
    41 43   
    42 44  notification_q = queue.Queue()
    43 45   
    44  -app = Flask(__name__, static_url_path="/var/www/change-detection/backend/static")
     46 +# Needs to be set this way because we also build and publish via pip
     47 +base_path = os.path.dirname(os.path.realpath(__file__))
     48 +app = Flask(__name__,
     49 + static_url_path="{}/static".format(base_path),
     50 + template_folder="{}/templates".format(base_path))
    45 51   
    46 52  # Stop browser caching of assets
    47 53  app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
    skipped 109 lines
    157 163   global datastore
    158 164   datastore = datastore_o
    159 165   
    160  - app.config.update(dict(DEBUG=True))
    161 166   #app.config.update(config or {})
    162 167   
    163 168   login_manager = flask_login.LoginManager(app)
    skipped 114 lines
    278 283   return response
    279 284   
    280 285   else:
    281  - from backend import forms
     286 + from changedetectionio import forms
    282 287   form = forms.quickWatchForm(request.form)
    283 288   
    284 289   output = render_template("watch-overview.html",
    skipped 59 lines
    344 349   def get_current_checksum_include_ignore_text(uuid):
    345 350   
    346 351   import hashlib
    347  - from backend import fetch_site_status
     352 + from changedetectionio import fetch_site_status
    348 353   
    349 354   # Get the most recent one
    350 355   newest_history_key = datastore.get_val(uuid, 'newest_history_key')
    skipped 20 lines
    371 376   @app.route("/edit/<string:uuid>", methods=['GET', 'POST'])
    372 377   @login_required
    373 378   def edit_page(uuid):
    374  - from backend import forms
     379 + from changedetectionio import forms
    375 380   form = forms.watchForm(request.form)
    376 381   
    377 382   # More for testing, possible to return the first/only
    skipped 95 lines
    473 478   @login_required
    474 479   def settings_page():
    475 480   
    476  - from backend import forms
    477  - from backend import content_fetcher
     481 + from changedetectionio import forms
     482 + from changedetectionio import content_fetcher
    478 483   
    479 484   form = forms.globalSettingsForm(request.form)
    480 485   
    skipped 241 lines
    722 727   @app.route("/static/<string:group>/<string:filename>", methods=['GET'])
    723 728   def static_content(group, filename):
    724 729   # These files should be in our subdirectory
    725  - full_path = os.path.realpath(__file__)
    726  - p = os.path.dirname(full_path)
    727  - 
    728 730   try:
    729  - return send_from_directory("{}/static/{}".format(p, group), filename=filename)
     731 + return send_from_directory("static/{}".format(group), filename=filename)
    730 732   except FileNotFoundError:
    731 733   abort(404)
    732 734   
    733 735   @app.route("/api/add", methods=['POST'])
    734 736   @login_required
    735 737   def api_watch_add():
    736  - from backend import forms
     738 + from changedetectionio import forms
    737 739   form = forms.quickWatchForm(request.form)
    738 740   
    739 741   if form.validate():
    skipped 81 lines
    821 823   while not app.config.exit.is_set():
    822 824   try:
    823 825   r = requests.post("https://changedetection.io/check-ver.php",
    824  - data={'version': datastore.data['version_tag'],
     826 + data={'version': __version__,
    825 827   'app_guid': datastore.data['app_guid'],
    826 828   'watch_count': len(datastore.data['watching'])
    827 829   },
    skipped 22 lines
    850 852   else:
    851 853   # Process notifications
    852 854   try:
    853  - from backend import notification
     855 + from changedetectionio import notification
    854 856   notification.process_notification(n_object, datastore)
    855 857   
    856 858   except Exception as e:
    skipped 3 lines
    860 862   
    861 863  # Thread runner to check every minute, look for new watches to feed into the Queue.
    862 864  def ticker_thread_check_time_launch_checks():
    863  - from backend import update_worker
     865 + from changedetectionio import update_worker
    864 866   
    865 867   # Spin up Workers.
    866 868   for _ in range(datastore.data['settings']['requests']['workers']):
    skipped 35 lines
  • ■ ■ ■ ■
    backend/content_fetcher.py changedetectionio/content_fetcher.py
    skipped 44 lines
    45 45   
    46 46  def available_fetchers():
    47 47   import inspect
    48  - from backend import content_fetcher
     48 + from changedetectionio import content_fetcher
    49 49   p=[]
    50 50   for name, obj in inspect.getmembers(content_fetcher):
    51 51   if inspect.isclass(obj):
    skipped 77 lines
  • backend/dev-docker/Dockerfile changedetectionio/dev-docker/Dockerfile
    Content is identical
  • backend/dev-docker/sleep.py changedetectionio/dev-docker/sleep.py
    Content is identical
  • ■ ■ ■ ■
    backend/fetch_site_status.py changedetectionio/fetch_site_status.py
    1 1  import time
    2  -from backend import content_fetcher
     2 +from changedetectionio import content_fetcher
    3 3  import hashlib
    4 4  from inscriptis import get_text
    5 5  import urllib3
    skipped 174 lines
  • ■ ■ ■ ■ ■ ■
    backend/forms.py changedetectionio/forms.py
    skipped 2 lines
    3 3  from wtforms import widgets
    4 4  from wtforms.validators import ValidationError
    5 5  from wtforms.fields import html5
    6  -from backend import content_fetcher
     6 +from changedetectionio import content_fetcher
    7 7  import re
    8 8   
    9 9  class StringListField(StringField):
    skipped 81 lines
    91 91   self.message = message
    92 92   
    93 93   def __call__(self, form, field):
    94  - from backend import content_fetcher
     94 + from changedetectionio import content_fetcher
    95 95   import urllib3.exceptions
    96 96   
    97 97   # Better would be a radiohandler that keeps a reference to each class
    skipped 103 lines
  • backend/html_tools.py changedetectionio/html_tools.py
    Content is identical
  • backend/notification.py changedetectionio/notification.py
    Content is identical
  • backend/pytest.ini changedetectionio/pytest.ini
    Content is identical
  • backend/run_all_tests.sh changedetectionio/run_all_tests.sh
    Content is identical
  • backend/static/images/Generic_Feed-icon.svg changedetectionio/static/images/Generic_Feed-icon.svg
  • backend/static/images/Google-Chrome-icon.png changedetectionio/static/images/Google-Chrome-icon.png
  • backend/static/images/favicon.ico changedetectionio/static/images/favicon.ico
  • backend/static/images/gradient-border.png changedetectionio/static/images/gradient-border.png
  • backend/static/images/pause.svg changedetectionio/static/images/pause.svg
  • backend/static/js/diff.js changedetectionio/static/js/diff.js
    Content is identical
  • backend/static/js/settings.js changedetectionio/static/js/settings.js
    Content is identical
  • backend/static/js/tabs.js changedetectionio/static/js/tabs.js
    Content is identical
  • backend/static/styles/.gitignore changedetectionio/static/styles/.gitignore
    Content is identical
  • backend/static/styles/diff.css changedetectionio/static/styles/diff.css
    Content is identical
  • backend/static/styles/diff.scss changedetectionio/static/styles/diff.scss
    Content is identical
  • backend/static/styles/package-lock.json changedetectionio/static/styles/package-lock.json
    Content is identical
  • backend/static/styles/package.json changedetectionio/static/styles/package.json
    Content is identical
  • backend/static/styles/pure-min.css changedetectionio/static/styles/pure-min.css
    Unable to diff as some line is too long.
  • backend/static/styles/styles.css changedetectionio/static/styles/styles.css
    Content is identical
  • backend/static/styles/styles.scss changedetectionio/static/styles/styles.scss
    Content is identical
  • ■ ■ ■ ■ ■ ■
    backend/store.py changedetectionio/store.py
    skipped 14 lines
    15 15  class ChangeDetectionStore:
    16 16   lock = Lock()
    17 17   
    18  - def __init__(self, datastore_path="/datastore", include_default_watches=True):
     18 + def __init__(self, datastore_path="/datastore", include_default_watches=True, version_tag="0.0.0"):
    19 19   self.needs_write = False
    20 20   self.datastore_path = datastore_path
    21 21   self.json_store_path = "{}/url-watches.json".format(self.datastore_path)
    skipped 50 lines
    72 72   'fetch_backend': None,
    73 73   }
    74 74   
    75  - if path.isfile('backend/source.txt'):
    76  - with open('backend/source.txt') as f:
     75 + if path.isfile('changedetectionio/source.txt'):
     76 + with open('changedetectionio/source.txt') as f:
    77 77   # Should be set in Dockerfile to look for /source.txt , this will give us the git commit #
    78 78   # So when someone gives us a backup file to examine, we know exactly what code they were running.
    79 79   self.__data['build_sha'] = f.read()
    skipped 40 lines
    120 120   self.add_watch(url='https://www.gov.uk/coronavirus', tag='Covid')
    121 121   self.add_watch(url='https://changedetection.io', tag='Tech news')
    122 122   
    123  - self.__data['version_tag'] = "0.38.2"
     123 + self.__data['version_tag'] = version_tag
    124 124   
    125 125   # Helper to remove password protection
    126 126   password_reset_lockfile = "{}/removepassword.lock".format(self.datastore_path)
    skipped 257 lines
  • backend/templates/_helpers.jinja changedetectionio/templates/_helpers.jinja
    Content is identical
  • backend/templates/base.html changedetectionio/templates/base.html
    Content is identical
  • backend/templates/diff.html changedetectionio/templates/diff.html
    Content is identical
  • backend/templates/edit.html changedetectionio/templates/edit.html
    Content is identical
  • backend/templates/import.html changedetectionio/templates/import.html
    Content is identical
  • backend/templates/login.html changedetectionio/templates/login.html
    Content is identical
  • backend/templates/preview.html changedetectionio/templates/preview.html
    Content is identical
  • backend/templates/scrub.html changedetectionio/templates/scrub.html
    Content is identical
  • backend/templates/settings.html changedetectionio/templates/settings.html
    Content is identical
  • backend/templates/watch-overview.html changedetectionio/templates/watch-overview.html
    Content is identical
  • backend/tests/__init__.py changedetectionio/tests/__init__.py
    Content is identical
  • ■ ■ ■ ■ ■ ■
    backend/tests/conftest.py changedetectionio/tests/conftest.py
    1 1  #!/usr/bin/python3
    2 2   
    3 3  import pytest
    4  -from backend import changedetection_app
    5  -from backend import store
     4 +from changedetectionio import changedetection_app
     5 +from changedetectionio import store
    6 6  import os
    7 7   
    8 8  # https://github.com/pallets/flask/blob/1.1.2/examples/tutorial/tests/test_auth.py
    skipped 50 lines
  • backend/tests/test_access_control.py changedetectionio/tests/test_access_control.py
    Content is identical
  • backend/tests/test_backend.py changedetectionio/tests/test_backend.py
    Content is identical
  • ■ ■ ■ ■
    backend/tests/test_css_selector.py changedetectionio/tests/test_css_selector.py
    skipped 46 lines
    47 47   
    48 48  # Test that the CSS extraction works how we expect, important here is the right placing of new lines \n's
    49 49  def test_css_filter_output():
    50  - from backend import fetch_site_status
     50 + from changedetectionio import fetch_site_status
    51 51   from inscriptis import get_text
    52 52   
    53 53   # Check text with sub-parts renders correctly
    skipped 76 lines
  • backend/tests/test_headers.py changedetectionio/tests/test_headers.py
    Content is identical
  • ■ ■ ■ ■
    backend/tests/test_ignore_regex_text.py changedetectionio/tests/test_ignore_regex_text.py
    skipped 9 lines
    10 10  # Unit test of the stripper
    11 11  # Always we are dealing in utf-8
    12 12  def test_strip_regex_text_func():
    13  - from backend import fetch_site_status
     13 + from changedetectionio import fetch_site_status
    14 14   
    15 15   test_content = """
    16 16   but sometimes we want to remove the lines.
    skipped 16 lines
  • ■ ■ ■ ■
    backend/tests/test_ignore_text.py changedetectionio/tests/test_ignore_text.py
    skipped 9 lines
    10 10  # Unit test of the stripper
    11 11  # Always we are dealing in utf-8
    12 12  def test_strip_text_func():
    13  - from backend import fetch_site_status
     13 + from changedetectionio import fetch_site_status
    14 14   
    15 15   test_content = """
    16 16   Some content
    skipped 138 lines
  • backend/tests/test_jsonpath_selector.py changedetectionio/tests/test_jsonpath_selector.py
    Content is identical
  • backend/tests/test_notification.py changedetectionio/tests/test_notification.py
    Content is identical
  • backend/tests/test_trigger.py changedetectionio/tests/test_trigger.py
    Content is identical
  • backend/tests/test_trigger_regex.py changedetectionio/tests/test_trigger_regex.py
    Content is identical
  • backend/tests/test_trigger_regex_with_filter.py changedetectionio/tests/test_trigger_regex_with_filter.py
    Content is identical
  • backend/tests/test_watch_fields_storage.py changedetectionio/tests/test_watch_fields_storage.py
    Content is identical
  • backend/tests/util.py changedetectionio/tests/util.py
    Content is identical
  • ■ ■ ■ ■ ■ ■
    backend/update_worker.py changedetectionio/update_worker.py
    skipped 13 lines
    14 14   super().__init__(*args, **kwargs)
    15 15   
    16 16   def run(self):
    17  - from backend import fetch_site_status
     17 + from changedetectionio import fetch_site_status
    18 18   
    19 19   update_handler = fetch_site_status.perform_site_check(datastore=self.datastore)
    20 20   
    skipped 6 lines
    27 27   
    28 28   else:
    29 29   self.current_uuid = uuid
    30  - from backend import content_fetcher
     30 + from changedetectionio import content_fetcher
    31 31   
    32 32   if uuid in list(self.datastore.data['watching'].keys()):
    33 33   
    skipped 72 lines
  • ■ ■ ■ ■ ■ ■
    requirements-dev.txt
     1 +pytest ~=6.2
     2 +pytest-flask ~=1.2
     3 + 
  • ■ ■ ■ ■ ■ ■
    requirements.txt
    1 1  chardet==2.3.0
    2 2  flask~= 1.0
    3  -pytest ~=6.2
    4  -pytest-flask ~=1.2
     3 + 
    5 4  eventlet>=0.31.0
    6 5  requests[socks] ~= 2.15
    7 6  validators
    skipped 13 lines
    21 20  bs4
    22 21   
    23 22  selenium ~= 3.141
     23 + 
  • ■ ■ ■ ■ ■ ■
    setup.py
     1 +#!/usr/bin/env python
     2 +import codecs
     3 +import os.path
     4 +import re
     5 +import sys
     6 + 
     7 +from setuptools import setup, find_packages
     8 + 
     9 +here = os.path.abspath(os.path.dirname(__file__))
     10 + 
     11 + 
     12 +def read(*parts):
     13 + return codecs.open(os.path.join(here, *parts), 'r').read()
     14 + 
     15 + 
     16 +def find_version(*file_paths):
     17 + version_file = read(*file_paths)
     18 + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
     19 + version_file, re.M)
     20 + if version_match:
     21 + return version_match.group(1)
     22 + raise RuntimeError("Unable to find version string.")
     23 + 
     24 + 
     25 +install_requires = open('requirements.txt').readlines()
     26 + 
     27 +setup(
     28 + name='changedetection.io',
     29 + version=find_version("changedetectionio", "__init__.py"),
     30 + description='Website change detection and monitoring service',
     31 + long_description=open('README-pip.md').read(),
     32 + long_description_content_type='text/markdown',
     33 + keywords='website change monitor for changes notification change detection '
     34 + 'alerts tracking website tracker change alert website and monitoring',
     35 + zip_safe=False,
     36 + entry_points={"console_scripts": ["changedetection.io=changedetection:main"]},
     37 + author='dgtlmoon',
     38 + url='https://changedetection.io',
     39 + scripts=['changedetection.py'],
     40 + packages=['changedetectionio'],
     41 + include_package_data=True,
     42 + install_requires=install_requires,
     43 + license="Apache License 2.0",
     44 + python_requires=">= 3.6",
     45 + classifiers=['Intended Audience :: Customer Service',
     46 + 'Intended Audience :: Developers',
     47 + 'Intended Audience :: Education',
     48 + 'Intended Audience :: End Users/Desktop',
     49 + 'Intended Audience :: Financial and Insurance Industry',
     50 + 'Intended Audience :: Healthcare Industry',
     51 + 'Intended Audience :: Information Technology',
     52 + 'Intended Audience :: Legal Industry',
     53 + 'Intended Audience :: Manufacturing',
     54 + 'Intended Audience :: Other Audience',
     55 + 'Intended Audience :: Religion',
     56 + 'Intended Audience :: Science/Research',
     57 + 'Intended Audience :: System Administrators',
     58 + 'Intended Audience :: Telecommunications Industry',
     59 + 'Topic :: Education',
     60 + 'Topic :: Internet',
     61 + 'Topic :: Internet :: WWW/HTTP :: Indexing/Search',
     62 + 'Topic :: Internet :: WWW/HTTP :: Site Management',
     63 + 'Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking',
     64 + 'Topic :: Internet :: WWW/HTTP :: Browsers',
     65 + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
     66 + 'Topic :: Office/Business',
     67 + 'Topic :: Other/Nonlisted Topic',
     68 + 'Topic :: Scientific/Engineering :: Information Analysis',
     69 + 'Topic :: Text Processing :: Markup :: HTML',
     70 + 'Topic :: Utilities'
     71 + ],
     72 +)
     73 + 
Please wait...
Page is in error, reload to recover