gitextract_1122jxp9/ ├── .dockerignore ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── actions/ │ │ └── extract-memory-report/ │ │ └── action.yml │ ├── dependabot.yml │ ├── nginx-reverse-proxy-test.conf │ ├── test/ │ │ └── Dockerfile-alpine │ └── workflows/ │ ├── codeql-analysis.yml │ ├── containers.yml │ ├── pypi-release.yml │ ├── test-container-build.yml │ ├── test-only.yml │ └── test-stack-reusable-workflow.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .ruff.toml ├── COMMERCIAL_LICENCE.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── README-pip.md ├── README.md ├── babel.cfg ├── changedetection.py ├── changedetectionio/ │ ├── .gitignore │ ├── PLUGIN_README.md │ ├── __init__.py │ ├── api/ │ │ ├── Import.py │ │ ├── Notifications.py │ │ ├── Search.py │ │ ├── Spec.py │ │ ├── SystemInfo.py │ │ ├── Tags.py │ │ ├── Watch.py │ │ ├── __init__.py │ │ └── auth.py │ ├── auth_decorator.py │ ├── blueprint/ │ │ ├── __init__.py │ │ ├── backups/ │ │ │ ├── __init__.py │ │ │ ├── restore.py │ │ │ └── templates/ │ │ │ ├── backup_create.html │ │ │ └── backup_restore.html │ │ ├── browser_steps/ │ │ │ ├── TODO.txt │ │ │ └── __init__.py │ │ ├── check_proxies/ │ │ │ └── __init__.py │ │ ├── imports/ │ │ │ ├── __init__.py │ │ │ ├── importer.py │ │ │ └── templates/ │ │ │ └── import.html │ │ ├── price_data_follower/ │ │ │ └── __init__.py │ │ ├── rss/ │ │ │ ├── __init__.py │ │ │ ├── _util.py │ │ │ ├── blueprint.py │ │ │ ├── main_feed.py │ │ │ ├── single_watch.py │ │ │ └── tag.py │ │ ├── settings/ │ │ │ ├── __init__.py │ │ │ └── templates/ │ │ │ ├── notification-log.html │ │ │ └── settings.html │ │ ├── tags/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── form.py │ │ │ └── templates/ │ │ │ ├── edit-tag.html │ │ │ └── groups-overview.html │ │ ├── ui/ │ │ │ ├── __init__.py │ │ │ ├── diff.py │ │ │ ├── edit.py │ │ │ ├── notification.py │ │ │ ├── preview.py │ │ │ ├── templates/ │ │ │ │ ├── clear_all_history.html │ │ │ │ ├── diff-offscreen-options.html │ │ │ │ ├── diff.html │ │ │ │ ├── edit.html │ │ │ │ └── preview.html │ │ │ └── views.py │ │ └── watchlist/ │ │ ├── __init__.py │ │ └── templates/ │ │ └── watch-overview.html │ ├── browser_steps/ │ │ ├── __init__.py │ │ └── browser_steps.py │ ├── conditions/ │ │ ├── __init__.py │ │ ├── blueprint.py │ │ ├── default_plugin.py │ │ ├── exceptions.py │ │ ├── form.py │ │ ├── pluggy_interface.py │ │ └── plugins/ │ │ ├── __init__.py │ │ ├── levenshtein_plugin.py │ │ └── wordcount_plugin.py │ ├── content_fetchers/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── exceptions/ │ │ │ └── __init__.py │ │ ├── playwright.py │ │ ├── puppeteer.py │ │ ├── requests.py │ │ ├── res/ │ │ │ ├── __init__.py │ │ │ ├── favicon-fetcher.js │ │ │ ├── lock-elements-sizing.js │ │ │ ├── stock-not-in-stock.js │ │ │ ├── unlock-elements-sizing.js │ │ │ └── xpath_element_scraper.js │ │ ├── screenshot_handler.py │ │ └── webdriver_selenium.py │ ├── custom_queue.py │ ├── diff/ │ │ ├── __init__.py │ │ └── tokenizers/ │ │ ├── __init__.py │ │ ├── natural_text.py │ │ └── words_and_html.py │ ├── favicon_utils.py │ ├── flask_app.py │ ├── forms.py │ ├── gc_cleanup.py │ ├── html_tools.py │ ├── is_safe_url.py │ ├── jinja2_custom/ │ │ ├── __init__.py │ │ ├── extensions/ │ │ │ ├── TimeExtension.py │ │ │ └── __init__.py │ │ ├── plugins/ │ │ │ ├── __init__.py │ │ │ └── regex.py │ │ └── safe_jinja.py │ ├── languages.py │ ├── model/ │ │ ├── App.py │ │ ├── Tag.py │ │ ├── Tags.py │ │ ├── Watch.py │ │ ├── __init__.py │ │ ├── persistence.py │ │ └── schema_utils.py │ ├── notification/ │ │ ├── __init__.py │ │ ├── apprise_plugin/ │ │ │ ├── __init__.py │ │ │ ├── assets.py │ │ │ ├── custom_handlers.py │ │ │ └── discord.py │ │ ├── email_helpers.py │ │ └── handler.py │ ├── notification_service.py │ ├── pluggy_interface.py │ ├── processors/ │ │ ├── README.md │ │ ├── __init__.py │ │ ├── base.py │ │ ├── exceptions.py │ │ ├── extract.py │ │ ├── image_ssim_diff/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── difference.py │ │ │ ├── edit_hook.py │ │ │ ├── forms.py │ │ │ ├── image_handler/ │ │ │ │ ├── __init__.py │ │ │ │ ├── isolated_libvips.py │ │ │ │ ├── isolated_opencv.py │ │ │ │ └── libvips_handler.py │ │ │ ├── preview.py │ │ │ ├── processor.py │ │ │ ├── templates/ │ │ │ │ └── image_ssim_diff/ │ │ │ │ ├── diff.html │ │ │ │ └── preview.html │ │ │ └── util.py │ │ ├── magic.py │ │ ├── restock_diff/ │ │ │ ├── __init__.py │ │ │ ├── api.yaml │ │ │ ├── forms.py │ │ │ ├── processor.py │ │ │ └── pure_python_extractor.py │ │ ├── templates/ │ │ │ └── extract.html │ │ └── text_json_diff/ │ │ ├── __init__.py │ │ ├── difference.py │ │ └── processor.py │ ├── pytest.ini │ ├── queue_handlers.py │ ├── queuedWatchMetaData.py │ ├── realtime/ │ │ ├── README.md │ │ ├── __init__.py │ │ ├── events.py │ │ └── socket_server.py │ ├── rss_tools.py │ ├── run_basic_tests.sh │ ├── run_custom_browser_url_tests.sh │ ├── run_proxy_tests.sh │ ├── run_socks_proxy_tests.sh │ ├── static/ │ │ ├── favicons/ │ │ │ ├── browserconfig.xml │ │ │ └── site.webmanifest │ │ ├── js/ │ │ │ ├── browser-steps.js │ │ │ ├── comparison-slider.js │ │ │ ├── conditions.js │ │ │ ├── csrf.js │ │ │ ├── diff-overview.js │ │ │ ├── diff-render.js │ │ │ ├── flask-toast-bridge.js │ │ │ ├── global-settings.js │ │ │ ├── hamburger-menu.js │ │ │ ├── language-selector.js │ │ │ ├── modal.js │ │ │ ├── notifications.js │ │ │ ├── plugins.js │ │ │ ├── preview.js │ │ │ ├── realtime.js │ │ │ ├── recheck-proxy.js │ │ │ ├── scheduler.js │ │ │ ├── search-modal.js │ │ │ ├── snippet-to-image.js │ │ │ ├── stepper.js │ │ │ ├── tabs.js │ │ │ ├── toast.js │ │ │ ├── toggle-theme.js │ │ │ ├── vis.js │ │ │ ├── visual-selector.js │ │ │ ├── watch-overview.js │ │ │ └── watch-settings.js │ │ └── styles/ │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── diff-image.css │ │ ├── diff.css │ │ ├── package.json │ │ ├── pure-min.css │ │ ├── scss/ │ │ │ ├── _settings.scss │ │ │ ├── diff-image.scss │ │ │ ├── diff.scss │ │ │ ├── parts/ │ │ │ │ ├── _action_sidebar.scss │ │ │ │ ├── _arrows.scss │ │ │ │ ├── _browser-steps.scss │ │ │ │ ├── _conditions_table.scss │ │ │ │ ├── _darkmode.scss │ │ │ │ ├── _diff_image.scss │ │ │ │ ├── _edit.scss │ │ │ │ ├── _extra_browsers.scss │ │ │ │ ├── _extra_proxies.scss │ │ │ │ ├── _hamburger_menu.scss │ │ │ │ ├── _language.scss │ │ │ │ ├── _lister_extra.scss │ │ │ │ ├── _login_form.scss │ │ │ │ ├── _love.scss │ │ │ │ ├── _menu.scss │ │ │ │ ├── _minitabs.scss │ │ │ │ ├── _modal.scss │ │ │ │ ├── _notification_bubble.scss │ │ │ │ ├── _pagination.scss │ │ │ │ ├── _preview_text_filter.scss │ │ │ │ ├── _search_modal.scss │ │ │ │ ├── _socket.scss │ │ │ │ ├── _spinners.scss │ │ │ │ ├── _tabs.scss │ │ │ │ ├── _toast.scss │ │ │ │ ├── _variables.scss │ │ │ │ ├── _visualselector.scss │ │ │ │ ├── _watch_table-mobile.scss │ │ │ │ ├── _watch_table.scss │ │ │ │ └── _widgets.scss │ │ │ └── styles.scss │ │ └── styles.css │ ├── store/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── file_saving_datastore.py │ │ └── updates.py │ ├── strtobool.py │ ├── templates/ │ │ ├── IMPORTANT.md │ │ ├── _common_fields.html │ │ ├── _helpers.html │ │ ├── base.html │ │ ├── edit/ │ │ │ ├── include_subtract.html │ │ │ └── text-options.html │ │ ├── login.html │ │ └── menu.html │ ├── test_cli_opts.sh │ ├── tests/ │ │ ├── __init__.py │ │ ├── apprise/ │ │ │ ├── test_apprise_asset.py │ │ │ └── test_apprise_custom_api_call.py │ │ ├── conftest.py │ │ ├── custom_browser_url/ │ │ │ ├── __init__.py │ │ │ └── test_custom_browser_url.py │ │ ├── fetchers/ │ │ │ ├── __init__.py │ │ │ ├── conftest.py │ │ │ ├── test_content.py │ │ │ └── test_custom_js_before_content.py │ │ ├── import/ │ │ │ └── spreadsheet.xlsx │ │ ├── itemprop_test_examples/ │ │ │ ├── README.md │ │ │ └── a.txt │ │ ├── plugins/ │ │ │ └── test_processor.py │ │ ├── proxy_list/ │ │ │ ├── __init__.py │ │ │ ├── conftest.py │ │ │ ├── proxies.json-example │ │ │ ├── squid-auth.conf │ │ │ ├── squid-passwords.txt │ │ │ ├── squid.conf │ │ │ ├── test_multiple_proxy.py │ │ │ ├── test_noproxy.py │ │ │ ├── test_proxy.py │ │ │ ├── test_proxy_noconnect.py │ │ │ └── test_select_custom_proxy.py │ │ ├── proxy_socks5/ │ │ │ ├── proxies.json-example │ │ │ ├── proxies.json-example-noauth │ │ │ ├── test_socks5_proxy.py │ │ │ └── test_socks5_proxy_sources.py │ │ ├── restock/ │ │ │ ├── __init__.py │ │ │ ├── conftest.py │ │ │ └── test_restock.py │ │ ├── smtp/ │ │ │ ├── smtp-test-server.py │ │ │ └── test_notification_smtp.py │ │ ├── test_access_control.py │ │ ├── test_add_replace_remove_filter.py │ │ ├── test_api.py │ │ ├── test_api_notification_urls_validation.py │ │ ├── test_api_notifications.py │ │ ├── test_api_openapi.py │ │ ├── test_api_search.py │ │ ├── test_api_security.py │ │ ├── test_api_tags.py │ │ ├── test_auth.py │ │ ├── test_automatic_follow_ldjson_price.py │ │ ├── test_backend.py │ │ ├── test_backup.py │ │ ├── test_basic_socketio.py │ │ ├── test_block_while_text_present.py │ │ ├── test_clone.py │ │ ├── test_commit_persistence.py │ │ ├── test_conditions.py │ │ ├── test_css_selector.py │ │ ├── test_datastore_isolation.py │ │ ├── test_element_removal.py │ │ ├── test_encoding.py │ │ ├── test_errorhandling.py │ │ ├── test_extract_csv.py │ │ ├── test_extract_regex.py │ │ ├── test_filter_exist_changes.py │ │ ├── test_filter_failure_notification.py │ │ ├── test_group.py │ │ ├── test_history_consistency.py │ │ ├── test_html_to_text.py │ │ ├── test_i18n.py │ │ ├── test_ignore.py │ │ ├── test_ignore_regex_text.py │ │ ├── test_ignore_text.py │ │ ├── test_ignorehyperlinks.py │ │ ├── test_ignorestatuscode.py │ │ ├── test_ignorewhitespace.py │ │ ├── test_import.py │ │ ├── test_jinja2.py │ │ ├── test_jsonpath_jq_selector.py │ │ ├── test_live_preview.py │ │ ├── test_nonrenderable_pages.py │ │ ├── test_notification.py │ │ ├── test_notification_errors.py │ │ ├── test_obfuscations.py │ │ ├── test_pdf.py │ │ ├── test_preview_endpoints.py │ │ ├── test_queue_handler.py │ │ ├── test_request.py │ │ ├── test_restock_itemprop.py │ │ ├── test_rss.py │ │ ├── test_rss_group.py │ │ ├── test_rss_reader_mode.py │ │ ├── test_rss_single_watch.py │ │ ├── test_scheduler.py │ │ ├── test_search.py │ │ ├── test_security.py │ │ ├── test_settings_tag_force_reprocess.py │ │ ├── test_share_watch.py │ │ ├── test_source.py │ │ ├── test_trigger.py │ │ ├── test_trigger_regex.py │ │ ├── test_trigger_regex_with_filter.py │ │ ├── test_ui.py │ │ ├── test_unique_lines.py │ │ ├── test_watch_edited_flag.py │ │ ├── test_watch_fields_storage.py │ │ ├── test_xpath_default_namespace.py │ │ ├── test_xpath_selector.py │ │ ├── test_xpath_selector_unit.py │ │ ├── unit/ │ │ │ ├── __init__.py │ │ │ ├── test-content/ │ │ │ │ ├── README.md │ │ │ │ ├── after-2.txt │ │ │ │ ├── after.txt │ │ │ │ └── before.txt │ │ │ ├── test_conditions.py │ │ │ ├── test_html_to_text.py │ │ │ ├── test_jinja2_security.py │ │ │ ├── test_notification_diff.py │ │ │ ├── test_restock_logic.py │ │ │ ├── test_scheduler.py │ │ │ ├── test_semver.py │ │ │ ├── test_time_extension.py │ │ │ ├── test_time_handler.py │ │ │ └── test_watch_model.py │ │ ├── util.py │ │ └── visualselector/ │ │ ├── __init__.py │ │ ├── conftest.py │ │ └── test_fetch_data.py │ ├── time_handler.py │ ├── translations/ │ │ ├── README.md │ │ ├── cs/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── de/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── en_GB/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── en_US/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── es/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── fr/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── it/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── ko/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── messages.pot │ │ ├── uk/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ ├── zh/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── messages.mo │ │ │ └── messages.po │ │ └── zh_Hant_TW/ │ │ └── LC_MESSAGES/ │ │ ├── messages.mo │ │ └── messages.po │ ├── validate_url.py │ ├── widgets/ │ │ ├── __init__.py │ │ ├── ternary_boolean.py │ │ └── test_custom_text.py │ ├── worker.py │ └── worker_pool.py ├── docker-compose.yml ├── docker-entrypoint.sh ├── docs/ │ ├── .gitignore │ ├── README.md │ ├── api-spec.yaml │ ├── api_v1/ │ │ └── index.html │ └── package.json ├── requirements.txt ├── runtime.txt ├── setup.cfg └── setup.py