├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── config.yml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── publish-develop-dockerhub.yml │ ├── publish-develop-docs.yml │ ├── publish-develop-ghcr.yml │ ├── publish-latest-dockerhub.yml │ ├── publish-latest-ghcr.yml │ ├── publish-release-docs.yml │ ├── test-docs.yml │ └── test-python.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .pylintrc ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── VERSION ├── conreq ├── __init__.py ├── app_template │ └── app_name │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── __init__.py │ │ ├── app.json │ │ ├── config.py │ │ ├── main │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ │ ├── navtabs.py │ │ ├── package.json │ │ ├── requirements.txt │ │ └── settings.py ├── asgi.py ├── core │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── permissions.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── arrs │ │ ├── __init__.py │ │ ├── base.py │ │ ├── helpers.py │ │ ├── radarr.py │ │ ├── sonarr.py │ │ └── tasks.py │ ├── base │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── fields.py │ │ ├── forms.py │ │ ├── helpers.py │ │ ├── management │ │ │ ├── __init__.py │ │ │ └── commands │ │ │ │ ├── __init__.py │ │ │ │ ├── preconfig_conreq.py │ │ │ │ ├── run_conreq.py │ │ │ │ └── start_conreq_app.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── static │ │ │ ├── css │ │ │ │ ├── dark.css │ │ │ │ ├── initialization.css │ │ │ │ ├── issues.css │ │ │ │ ├── libraries │ │ │ │ │ ├── izi_toast_1.4.0.min.css │ │ │ │ │ └── magnific_popup_1.1.0.css │ │ │ │ ├── login.css │ │ │ │ ├── main.css │ │ │ │ ├── main_slim.css │ │ │ │ ├── manage_users.css │ │ │ │ ├── mobile.css │ │ │ │ ├── modal.css │ │ │ │ ├── more_info.css │ │ │ │ ├── navbar.css │ │ │ │ ├── posters.css │ │ │ │ ├── server_settings.css │ │ │ │ ├── sidebar.css │ │ │ │ ├── simple_posters.css │ │ │ │ └── viewport.css │ │ │ ├── icons │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── apple-touch-icon-114x114-precomposed.png │ │ │ │ ├── apple-touch-icon-114x114.png │ │ │ │ ├── apple-touch-icon-120x120-precomposed.png │ │ │ │ ├── apple-touch-icon-120x120.png │ │ │ │ ├── apple-touch-icon-144x144-precomposed.png │ │ │ │ ├── apple-touch-icon-144x144.png │ │ │ │ ├── apple-touch-icon-152x152-precomposed.png │ │ │ │ ├── apple-touch-icon-152x152.png │ │ │ │ ├── apple-touch-icon-180x180-precomposed.png │ │ │ │ ├── apple-touch-icon-180x180.png │ │ │ │ ├── apple-touch-icon-57x57-precomposed.png │ │ │ │ ├── apple-touch-icon-57x57.png │ │ │ │ ├── apple-touch-icon-60x60-precomposed.png │ │ │ │ ├── apple-touch-icon-60x60.png │ │ │ │ ├── apple-touch-icon-72x72-precomposed.png │ │ │ │ ├── apple-touch-icon-72x72.png │ │ │ │ ├── apple-touch-icon-76x76-precomposed.png │ │ │ │ ├── apple-touch-icon-76x76.png │ │ │ │ ├── apple-touch-icon-precomposed.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── browserconfig.xml │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ ├── favicon.ico │ │ │ │ ├── maskable.png │ │ │ │ ├── mstile-150x150.png │ │ │ │ ├── mstile-310x310.png │ │ │ │ ├── mstile-70x70.png │ │ │ │ ├── safari-pinned-tab.svg │ │ │ │ ├── site.webmanifest │ │ │ │ └── standard.png │ │ │ ├── images │ │ │ │ ├── conreq_logo.png │ │ │ │ ├── conreq_logo_dark.png │ │ │ │ ├── person_placeholder.png │ │ │ │ ├── poster_placeholder.png │ │ │ │ └── transparent.png │ │ │ └── js │ │ │ │ ├── client_websockets.js │ │ │ │ ├── events_click.js │ │ │ │ ├── events_generic.js │ │ │ │ ├── generic_functions.js │ │ │ │ ├── initialization.js │ │ │ │ ├── libraries │ │ │ │ ├── dom_purify_2.2.min.js │ │ │ │ ├── dom_purify_2.2.min.map │ │ │ │ ├── izi_toast_1.4.0.min.js │ │ │ │ └── magnific_popup_1.1.0.min.js │ │ │ │ ├── modal.js │ │ │ │ ├── more_info.js │ │ │ │ ├── sidebar.js │ │ │ │ ├── sign_up.js │ │ │ │ ├── toast_messages.js │ │ │ │ └── viewport.js │ │ ├── tasks.py │ │ ├── templates │ │ │ └── primary │ │ │ │ ├── base_app.html │ │ │ │ ├── head_content.html │ │ │ │ ├── head_content_slim.html │ │ │ │ ├── icons.html │ │ │ │ ├── loading_animation.html │ │ │ │ ├── loading_animation_container.html │ │ │ │ ├── navbar.html │ │ │ │ ├── searchbar.html │ │ │ │ └── sidebar.html │ │ ├── templatetags │ │ │ ├── __init__.py │ │ │ └── conreq_tags.py │ │ ├── urls.py │ │ ├── validators.py │ │ └── views.py │ ├── discover │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── helpers.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── urls.py │ │ └── views.py │ ├── issue_reporting │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── helpers.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_auto_20210203_1912.py │ │ │ ├── 0003_auto_20210203_1917.py │ │ │ ├── 0004_auto_20210203_2153.py │ │ │ ├── 0005_auto_20210203_2248.py │ │ │ ├── 0006_auto_20210203_2302.py │ │ │ ├── 0007_auto_20210204_0207.py │ │ │ ├── 0008_auto_20210206_0020.py │ │ │ ├── 0009_auto_20210206_0405.py │ │ │ ├── 0010_alter_reportedissue_id.py │ │ │ ├── 0011_auto_20210621_2105.py │ │ │ ├── 0012_rename_manually_resolved_reportedissue_resolved.py │ │ │ ├── 0013_remove_reportedissue_resolutions.py │ │ │ ├── 0014_reportedissue_resolutions.py │ │ │ ├── 0015_auto_20210705_2244.py │ │ │ ├── 0016_auto_20210705_2248.py │ │ │ ├── 0017_remove_reportedissue_source.py │ │ │ ├── 0018_alter_reportedissue_resolutions.py │ │ │ ├── 0019_reportedissue_auto_resolve_in_progress.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tasks.py │ │ ├── urls.py │ │ └── views.py │ ├── manage_users │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_profile_http_header_auth_user.py │ │ │ ├── 0003_alter_profile_id.py │ │ │ ├── 0004_rename_http_header_auth_user_profile_externally_authenticated.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── urls.py │ │ └── views.py │ ├── more_info │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── helpers.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── urls.py │ │ └── views.py │ ├── password_reset │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── forms.py │ │ ├── templates │ │ │ └── conreq │ │ │ │ ├── password_reset.html │ │ │ │ ├── password_reset_confirm.html │ │ │ │ └── password_reset_sent.html │ │ ├── urls.py │ │ └── views.py │ ├── pwa │ │ ├── LICENSE.md │ │ ├── __init__.py │ │ ├── app_settings.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── templates │ │ │ ├── manifest.json │ │ │ ├── offline.html │ │ │ ├── pwa.html │ │ │ └── serviceworker.js │ │ ├── templatetags │ │ │ ├── __init__.py │ │ │ └── pwa.py │ │ ├── urls.py │ │ └── views.py │ ├── search │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── urls.py │ │ └── views.py │ ├── server_settings │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_auto_20210118_2004.py │ │ │ ├── 0003_conreqconfig_conreq_initialized.py │ │ │ ├── 0004_auto_20210119_1742.py │ │ │ ├── 0005_auto_20210127_0107.py │ │ │ ├── 0006_auto_20210130_0236.py │ │ │ ├── 0007_conreqconfig_conreq_http_header_auth.py │ │ │ ├── 0008_auto_20210203_0053.py │ │ │ ├── 0009_auto_20210204_1938.py │ │ │ ├── 0010_auto_20210205_2326.py │ │ │ ├── 0011_auto_20210205_2343.py │ │ │ ├── 0012_auto_20210209_2144.py │ │ │ ├── 0013_conreqconfig_conreq_allow_tv_specials.py │ │ │ ├── 0014_auto_20210313_0231.py │ │ │ ├── 0015_remove_conreqconfig_conreq_app_name.py │ │ │ ├── 0016_alter_conreqconfig_id.py │ │ │ ├── 0017_remove_conreqconfig_conreq_dark_theme.py │ │ │ ├── 0018_auto_20210428_2039.py │ │ │ ├── 0019_remove_conreqconfig_conreq_api_key.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── urls.py │ │ └── views.py │ ├── sign_up │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── forms.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── templates │ │ │ ├── modal │ │ │ │ ├── default.html │ │ │ │ └── manage_user.html │ │ │ └── registration │ │ │ │ ├── initialization.html │ │ │ │ ├── sign_in.html │ │ │ │ └── sign_up.html │ │ ├── urls.py │ │ └── views.py │ ├── tmdb │ │ ├── __init__.py │ │ ├── base.py │ │ ├── discovery.py │ │ ├── preset_filters.py │ │ ├── search.py │ │ └── templates │ │ │ ├── cards │ │ │ ├── artwork.html │ │ │ ├── casted.html │ │ │ ├── issue.html │ │ │ ├── person.html │ │ │ ├── poster.html │ │ │ └── review.html │ │ │ ├── compressed │ │ │ └── simple_posters_css.html │ │ │ ├── dropdown │ │ │ └── item.html │ │ │ ├── modal │ │ │ ├── content_preview.html │ │ │ ├── discover_filter_advanced.html │ │ │ ├── discover_filter_simple.html │ │ │ ├── report_issue.html │ │ │ └── series_selection.html │ │ │ └── viewport │ │ │ ├── components │ │ │ ├── more_info_collection.html │ │ │ ├── more_info_recommended.html │ │ │ ├── server_settings_radarr.html │ │ │ └── server_settings_sonarr.html │ │ │ ├── discover.html │ │ │ ├── manage_users.html │ │ │ ├── more_info.html │ │ │ ├── person.html │ │ │ ├── reported_issues.html │ │ │ ├── requests.html │ │ │ ├── search.html │ │ │ └── server_settings.html │ ├── user_requests │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── helpers.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_userrequest_date_requested.py │ │ │ ├── 0003_alter_userrequest_id.py │ │ │ ├── 0004_alter_userrequest_requested_by.py │ │ │ ├── 0005_remove_userrequest_source.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tasks.py │ │ ├── urls.py │ │ └── views.py │ └── websockets │ │ ├── __init__.py │ │ └── consumers.py ├── settings.py ├── urls.py └── utils │ ├── __init__.py │ ├── cache.py │ ├── database.py │ ├── debug.py │ ├── environment.py │ ├── generic.py │ ├── log.py │ ├── threads.py │ └── user_apps.py ├── docs ├── mkdocs.yml └── src │ ├── configure │ ├── env_vars.md │ ├── mysql.md │ ├── organizr.md │ ├── static_files.md │ └── webserver.md │ ├── contrib │ ├── apps.md │ └── docs.md │ ├── develop │ ├── build_docker.md │ ├── creating_apps.md │ └── run_conreq.md │ ├── dictionary.txt │ ├── index.md │ └── install │ ├── docker.md │ └── nssm.md ├── manage.py ├── misc ├── branding │ ├── circle.ai │ ├── circle.png │ ├── circle_outline.ai │ ├── circle_outline.png │ ├── conreq_logo.png │ ├── conreq_logo.psd │ ├── conreq_logo_dark.png │ ├── conreq_logo_dark.psd │ ├── conreq_square.ai │ ├── conreq_square.png │ ├── no_bg_dark.ai │ ├── no_bg_dark.png │ ├── no_bg_gradient.ai │ ├── no_bg_gradient.png │ ├── no_bg_light.ai │ ├── no_bg_light.png │ ├── rounded_square.ai │ ├── rounded_square.png │ ├── rounded_square_outline.ai │ └── rounded_square_outline.png ├── legal │ ├── fontawesome_attribution.js │ └── fontawesome_license.txt ├── other │ ├── person_placeholder.psd │ └── poster_placeholder.psd └── screenshots │ ├── desktop_discover.png │ ├── desktop_modal_episode_selection.png │ ├── desktop_modal_filter.png │ ├── desktop_modal_preview.png │ ├── desktop_more_info.png │ ├── desktop_registration.png │ ├── desktop_sign_in.png │ ├── mobile_discover.png │ ├── mobile_modal_episode_selection.png │ ├── mobile_modal_filter.png │ ├── mobile_more_info.png │ ├── mobile_registration.png │ ├── mobile_sidebar.png │ └── mobile_sign_in.png ├── requirements.txt └── requirements ├── dev.txt ├── docs.txt └── main.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Archmonger] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug with Conreq software. 3 | labels: [Bug] 4 | body: 5 | - type: input 6 | attributes: 7 | label: Conreq Version 8 | description: What version did you encounter this issue on? 9 | placeholder: For example... 1.25.0 10 | validations: 11 | required: true 12 | 13 | - type: dropdown 14 | attributes: 15 | label: Conreq Branch 16 | options: 17 | - Latest / Main 18 | - Develop 19 | - Other 20 | validations: 21 | required: true 22 | 23 | - type: dropdown 24 | attributes: 25 | label: Server Operating System 26 | multiple: true 27 | options: 28 | - Windows 29 | - Linux 30 | - FreeBSD 31 | - MacOS 32 | - N/A 33 | validations: 34 | required: true 35 | 36 | - type: dropdown 37 | attributes: 38 | label: Client Operating System 39 | multiple: true 40 | options: 41 | - Windows 42 | - Linux 43 | - FreeBSD 44 | - MacOS 45 | - Android 46 | - iOS 47 | - N/A 48 | validations: 49 | required: true 50 | 51 | - type: dropdown 52 | attributes: 53 | label: Web Browser(s) 54 | multiple: true 55 | options: 56 | - Firefox 57 | - Chrome 58 | - Safari 59 | - Microsoft Edge 60 | - N/A 61 | validations: 62 | required: true 63 | 64 | - type: textarea 65 | attributes: 66 | label: Description 67 | validations: 68 | required: true 69 | 70 | - type: textarea 71 | attributes: 72 | label: Log Info 73 | description: Copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 74 | render: shell 75 | validations: 76 | required: false 77 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Feature Request 4 | url: https://github.com/Archmonger/Conreq/discussions/categories/feature-requests 5 | about: Click "New Discussion" to create a feature request. See an idea you like? Vote for it! 6 | - name: Discord Support 7 | url: https://discord.gg/gQhGZzEjmX 8 | about: Ask a question within the support channel. 9 | - name: GitHub Community Support 10 | url: https://github.com/Archmonger/Conreq/discussions/categories/support 11 | about: Click "New Discussion" to create a support ticket. 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/requirements/" 5 | schedule: 6 | interval: "daily" 7 | target-branch: "main" 8 | open-pull-requests-limit: 20 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | ## Checklist 6 | 7 | Please update this checklist as you complete each item: 8 | 9 | - [ ] Tests have been developed for bug fixes or new functionality. 10 | - [ ] The changelog has been updated, if necessary. 11 | - [ ] Documentation has been updated, if necessary. 12 | - [ ] GitHub Issues closed by this PR have been linked. 13 | 14 | By submitting this pull request I agree that all contributions comply with this project's open source license(s). 15 | -------------------------------------------------------------------------------- /.github/workflows/publish-develop-dockerhub.yml: -------------------------------------------------------------------------------- 1 | name: Publish Develop to DockerHub 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Set up QEMU 13 | uses: docker/setup-qemu-action@v3 14 | - name: Set up Docker Buildx 15 | uses: docker/setup-buildx-action@v3 16 | - name: Login to Docker Hub 17 | uses: docker/login-action@v3 18 | with: 19 | username: ${{ secrets.DOCKERHUB_USERNAME }} 20 | password: ${{ secrets.DOCKERHUB_TOKEN }} 21 | - name: Build and push to Docker Hub 22 | uses: docker/build-push-action@v6 23 | with: 24 | push: true 25 | tags: archmonger/conreq:develop 26 | -------------------------------------------------------------------------------- /.github/workflows/publish-develop-docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish Develop Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: 3.x 17 | - run: pip install -r requirements/docs.txt 18 | - name: Publish Develop Docs 19 | run: | 20 | git config user.name github-actions 21 | git config user.email github-actions@github.com 22 | cd docs 23 | mike deploy --push develop 24 | -------------------------------------------------------------------------------- /.github/workflows/publish-develop-ghcr.yml: -------------------------------------------------------------------------------- 1 | name: Publish Develop to GHCR 2 | 3 | on: 4 | push: 5 | branches: 6 | - develmainop 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Set up QEMU 13 | uses: docker/setup-qemu-action@v3 14 | - name: Set up Docker Buildx 15 | uses: docker/setup-buildx-action@v3 16 | - name: Login to GitHub Container Registry 17 | uses: docker/login-action@v3 18 | with: 19 | registry: ghcr.io 20 | username: ${{ github.actor }} 21 | password: ${{ github.token }} 22 | - name: Build and push to GitHub Container Registry 23 | uses: docker/build-push-action@v6 24 | with: 25 | push: true 26 | tags: ghcr.io/archmonger/conreq:develop 27 | -------------------------------------------------------------------------------- /.github/workflows/publish-latest-dockerhub.yml: -------------------------------------------------------------------------------- 1 | name: Publish Latest to DockerHub 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | docker: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Set up QEMU 12 | uses: docker/setup-qemu-action@v3 13 | - name: Set up Docker Buildx 14 | uses: docker/setup-buildx-action@v3 15 | - name: Login to Docker Hub 16 | uses: docker/login-action@v3 17 | with: 18 | username: ${{ secrets.DOCKERHUB_USERNAME }} 19 | password: ${{ secrets.DOCKERHUB_TOKEN }} 20 | - name: Build and push to Docker Hub 21 | uses: docker/build-push-action@v6 22 | with: 23 | push: true 24 | tags: archmonger/conreq:latest,archmonger/conreq:${{ github.event.release.tag_name }} 25 | -------------------------------------------------------------------------------- /.github/workflows/publish-latest-ghcr.yml: -------------------------------------------------------------------------------- 1 | name: Publish Latest to GHCR 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | docker: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Set up QEMU 12 | uses: docker/setup-qemu-action@v3 13 | - name: Set up Docker Buildx 14 | uses: docker/setup-buildx-action@v3 15 | - name: Login to Docker Hub 16 | uses: docker/login-action@v3 17 | with: 18 | registry: ghcr.io 19 | username: ${{ github.actor }} 20 | password: ${{ github.token }} 21 | - name: Build and push to Docker Hub 22 | uses: docker/build-push-action@v6 23 | with: 24 | push: true 25 | tags: ghcr.io/archmonger/conreq:latest,ghcr.io/archmonger/conreq:${{ github.event.release.tag_name }} 26 | -------------------------------------------------------------------------------- /.github/workflows/publish-release-docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release Docs 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: 3.x 17 | - run: pip install -r requirements/docs.txt 18 | - name: Publish ${{ github.event.release.name }} Docs 19 | run: | 20 | git config user.name github-actions 21 | git config user.email github-actions@github.com 22 | cd docs 23 | mike deploy --push --update-aliases ${{ github.event.release.name }} latest 24 | -------------------------------------------------------------------------------- /.github/workflows/test-docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | schedule: 11 | - cron: "0 0 * * 0" 12 | 13 | jobs: 14 | docs: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | - uses: actions/setup-python@v5 21 | with: 22 | python-version: 3.x 23 | - name: Check docs build 24 | run: | 25 | pip install -r requirements/docs.txt 26 | linkcheckMarkdown docs/ -v -r 27 | linkcheckMarkdown README.md -v -r 28 | linkcheckMarkdown CHANGELOG.md -v -r 29 | cd docs 30 | mkdocs build --strict 31 | -------------------------------------------------------------------------------- /.github/workflows/test-python.yml: -------------------------------------------------------------------------------- 1 | name: Django Tests 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | max-parallel: 4 14 | matrix: 15 | python-version: [3.11] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - name: Install Dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install -r requirements.txt 27 | - name: Run Tests 28 | run: | 29 | python manage.py test --noinput --parallel 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Django # 2 | logs 3 | *.log 4 | *.pot 5 | *.pyc 6 | .dccache 7 | __pycache__ 8 | *.sqlite3 9 | data/ 10 | 11 | # Temp files 12 | *.tmp 13 | 14 | # Docker config folder 15 | config/ 16 | 17 | # Backup files # 18 | *.bak 19 | *_bak/ 20 | 21 | # If you are using PyCharm # 22 | .idea/**/workspace.xml 23 | .idea/**/tasks.xml 24 | .idea/dictionaries 25 | .idea/**/dataSources/ 26 | .idea/**/dataSources.ids 27 | .idea/**/dataSources.xml 28 | .idea/**/dataSources.local.xml 29 | .idea/**/sqlDataSources.xml 30 | .idea/**/dynamic.xml 31 | .idea/**/uiDesigner.xml 32 | .idea/**/gradle.xml 33 | .idea/**/libraries 34 | *.iws /out/ 35 | 36 | # Python # 37 | *.py[cod] 38 | *$py.class 39 | 40 | # Distribution / packaging 41 | .Python build/ 42 | develop-eggs/ 43 | dist/ 44 | downloads/ 45 | eggs/ 46 | .eggs/ 47 | lib/ 48 | lib64/ 49 | parts/ 50 | sdist/ 51 | var/ 52 | wheels/ 53 | *.egg-info/ 54 | .installed.cfg 55 | *.egg 56 | *.manifest 57 | *.spec 58 | 59 | # Installer logs 60 | pip-log.txt 61 | pip-delete-this-directory.txt 62 | 63 | # Unit test / coverage reports 64 | htmlcov/ 65 | .tox/ 66 | .coverage 67 | .coverage.* 68 | .cache 69 | .pytest_cache/ 70 | nosetests.xml 71 | coverage.xml 72 | *.cover 73 | .hypothesis/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery 82 | celerybeat-schedule.* 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Environments 88 | .env/ 89 | .venv/ 90 | env/ 91 | venv/ 92 | ENV/ 93 | env.bak/ 94 | venv.bak/ 95 | 96 | # mypy 97 | .mypy_cache/ 98 | 99 | # Sublime Text # 100 | *.tmlanguage.cache 101 | *.tmPreferences.cache 102 | *.stTheme.cache 103 | *.sublime-workspace 104 | *.sublime-project 105 | 106 | # sftp configuration file 107 | sftp-config.json 108 | 109 | # Package control specific files Package 110 | Control.last-run 111 | Control.ca-list 112 | Control.ca-bundle 113 | Control.system-ca-bundle 114 | GitHub.sublime-settings 115 | 116 | # Visual Studio Code # 117 | .vscode/* 118 | !.vscode/settings.json 119 | !.vscode/extensions.json 120 | .history/ 121 | %SystemDrive% 122 | 123 | # Mac file system 124 | .DS_Store/ 125 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: "v4.6.0" 4 | hooks: 5 | - id: check-merge-conflict 6 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | ignore=migrations 3 | disable=broad-except, fixme 4 | load-plugins=pylint_django 5 | django-settings-module=conreq.settings 6 | [FORMAT] 7 | max-line-length=150 -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "irongeek.vscode-env", 4 | "eamodio.gitlens", 5 | "github.vscode-pull-request-github", 6 | "wholroyd.jinja", 7 | "esbenp.prettier-vscode", 8 | "ms-python.vscode-pylance", 9 | "ms-python.python", 10 | "mrmlnc.vscode-autoprefixer", 11 | "gruntfuggly.todo-tree" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.detectIndentation": false, 3 | "editor.formatOnSave": true, 4 | "python.languageServer": "Pylance", 5 | "terminal.integrated.scrollback": 2000, 6 | "git.autofetch": true, 7 | "prettier.tabWidth": 4, 8 | "prettier.useTabs": true, 9 | "prettier.endOfLine": "auto", 10 | "autoprefixer.formatOnSave": true, 11 | "files.associations": { 12 | "**/requirements/*.txt": "pip-requirements", 13 | }, 14 | "[jsonc]": { 15 | "editor.defaultFormatter": "vscode.json-language-features" 16 | }, 17 | "[json]": { 18 | "editor.defaultFormatter": "vscode.json-language-features" 19 | }, 20 | "[javascript]": { 21 | "editor.defaultFormatter": "esbenp.prettier-vscode" 22 | }, 23 | "[css]": { 24 | "editor.defaultFormatter": "esbenp.prettier-vscode" 25 | }, 26 | "[python]": { 27 | "editor.defaultFormatter": "charliermarsh.ruff" 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/CHANGELOG.md -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # FROM ghcr.io/linuxserver/baseimage-alpine:3.20 2 | FROM python:3.11.9-alpine3.20 3 | 4 | ENV DATA_DIR=/config DEBUG=False 5 | 6 | COPY ./manage.py /app/manage.py 7 | COPY ./VERSION /app/VERSION 8 | COPY ./LICENSE /app/LICENSE 9 | COPY ./conreq/ /app/conreq/ 10 | COPY ./requirements/ /app/conreq/requirements/ 11 | 12 | RUN \ 13 | echo "**** Install build dependencies ****" \ 14 | && \ 15 | apk add --no-cache --virtual=build-dependencies \ 16 | bsd-compat-headers \ 17 | build-base \ 18 | cargo \ 19 | curl \ 20 | g++ \ 21 | gcc \ 22 | git \ 23 | jq \ 24 | libev-dev \ 25 | libffi-dev \ 26 | openssl-dev \ 27 | && \ 28 | echo "**** Install Linux packages ****" \ 29 | && \ 30 | apk add --no-cache \ 31 | freetype-dev \ 32 | fribidi-dev \ 33 | harfbuzz-dev \ 34 | jpeg-dev \ 35 | lcms2-dev \ 36 | openjpeg-dev \ 37 | tcl-dev \ 38 | tiff-dev \ 39 | tk-dev \ 40 | zlib-dev \ 41 | && \ 42 | echo "**** Install Python dependencies ****" \ 43 | && \ 44 | pip3 install --no-cache-dir -U -r /app/conreq/requirements/main.txt \ 45 | && \ 46 | echo "**** Cleanup ****" \ 47 | && \ 48 | apk del --purge \ 49 | build-dependencies \ 50 | && \ 51 | rm -rf \ 52 | /root/.cache \ 53 | /root/.cargo \ 54 | /tmp/* 55 | 56 | EXPOSE 7575 57 | 58 | WORKDIR /app/ 59 | CMD ["sh", "-c", "python3 manage.py run_conreq --uid ${PUID:=99} --gid ${PGID:=100}"] 60 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.21.1 2 | -------------------------------------------------------------------------------- /conreq/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/__init__.py -------------------------------------------------------------------------------- /conreq/app_template/app_name/.gitignore: -------------------------------------------------------------------------------- 1 | # Django # 2 | logs 3 | *.log 4 | *.pot 5 | *.pyc 6 | .dccache 7 | __pycache__ 8 | db.sqlite3 9 | media 10 | cache 11 | collectstatic 12 | data 13 | settings.json 14 | 15 | # Backup files # 16 | *.bak 17 | 18 | # If you are using PyCharm # 19 | .idea/**/workspace.xml 20 | .idea/**/tasks.xml 21 | .idea/dictionaries 22 | .idea/**/dataSources/ 23 | .idea/**/dataSources.ids 24 | .idea/**/dataSources.xml 25 | .idea/**/dataSources.local.xml 26 | .idea/**/sqlDataSources.xml 27 | .idea/**/dynamic.xml 28 | .idea/**/uiDesigner.xml 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | *.iws /out/ 32 | 33 | # Python # 34 | *.py[cod] 35 | *$py.class 36 | 37 | # Distribution / packaging 38 | .Python build/ 39 | develop-eggs/ 40 | dist/ 41 | downloads/ 42 | eggs/ 43 | .eggs/ 44 | lib/ 45 | lib64/ 46 | parts/ 47 | sdist/ 48 | var/ 49 | wheels/ 50 | *.egg-info/ 51 | .installed.cfg 52 | *.egg 53 | *.manifest 54 | *.spec 55 | 56 | # Installer logs 57 | pip-log.txt 58 | pip-delete-this-directory.txt 59 | 60 | # Unit test / coverage reports 61 | htmlcov/ 62 | .tox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | .pytest_cache/ 67 | nosetests.xml 68 | coverage.xml 69 | *.cover 70 | .hypothesis/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery 79 | celerybeat-schedule.* 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # mkdocs documentation 94 | /site 95 | 96 | # mypy 97 | .mypy_cache/ 98 | 99 | # Sublime Text # 100 | *.tmlanguage.cache 101 | *.tmPreferences.cache 102 | *.stTheme.cache 103 | *.sublime-workspace 104 | *.sublime-project 105 | 106 | # sftp configuration file 107 | sftp-config.json 108 | 109 | # Package control specific files Package 110 | Control.last-run 111 | Control.ca-list 112 | Control.ca-bundle 113 | Control.system-ca-bundle 114 | GitHub.sublime-settings 115 | 116 | # Visual Studio Code # 117 | .vscode 118 | .vscode/* 119 | .vscode/settings.json 120 | .vscode/tasks.json 121 | .vscode/launch.json 122 | .vscode/extensions.json 123 | .history 124 | %SystemDrive% 125 | 126 | # Mac file system 127 | .DS_Store -------------------------------------------------------------------------------- /conreq/app_template/app_name/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/app_template/app_name/__init__.py -------------------------------------------------------------------------------- /conreq/app_template/app_name/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "app_name": "", 3 | "app_version": "", 4 | "description": "", 5 | "notice_msg": "", 6 | "gh_author": "", 7 | "repository_url": "", 8 | "homepage_url": "", 9 | "support_url": "", 10 | "donation_url": "", 11 | "env_variables": [], 12 | "categories": [], 13 | "development_stage": "", 14 | "mobile_compatible": false, 15 | "min_conreq_version": "", 16 | "tested_conreq_version": "", 17 | "max_conreq_version": "", 18 | "optional_apps": [""], 19 | "required_apps": [""], 20 | "incompatible_apps": [""], 21 | "incompatible_app_categories": [] 22 | } 23 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/config.py: -------------------------------------------------------------------------------- 1 | # App configuration parameters used by Conreq 2 | HEAD_TEMPLATES = [] 3 | BODY_TEMPLATES = [] 4 | INIT_COMMANDS = [] 5 | PRECONFIGURE_COMMANDS = [] 6 | SPLASH_TEMPLATE = "" 7 | LOGIN_TEMPLATE = "" 8 | BASE_TEMPLATE = "" 9 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/app_template/app_name/main/__init__.py -------------------------------------------------------------------------------- /conreq/app_template/app_name/main/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/main/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class {{ camel_case_app_name }}Config(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = '{{ app_name }}.main' 7 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/main/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/app_template/app_name/main/migrations/__init__.py -------------------------------------------------------------------------------- /conreq/app_template/app_name/main/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/main/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/main/urls.py: -------------------------------------------------------------------------------- 1 | app_name = "{{ app_name }}" 2 | urlpattern = [] 3 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/main/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/navtabs.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | USER_NAVTABS = [] 4 | ADMIN_NAVTABS = [] 5 | CUSTOM_NAVTABS = {} 6 | SETTINGS_TABS = [] 7 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /conreq/app_template/app_name/requirements.txt: -------------------------------------------------------------------------------- 1 | # Any requirements in here will be automatically installed by Conreq -------------------------------------------------------------------------------- /conreq/app_template/app_name/settings.py: -------------------------------------------------------------------------------- 1 | # CAUTION: Anything in here will get automatically added into Conreq's Django settings file. 2 | # For more information, see the following docs: 3 | # https://docs.djangoproject.com/en/3.2/topics/settings/ 4 | # https://github.com/sobolevn/django-split-settings 5 | -------------------------------------------------------------------------------- /conreq/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for Conreq project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | # pylint: disable=wrong-import-position 10 | from django.core.asgi import get_asgi_application 11 | from django.urls import path 12 | 13 | # Fetch ASGI application before importing dependencies that require ORM models. 14 | django_asgi_app = get_asgi_application() 15 | 16 | from channels.auth import AuthMiddlewareStack 17 | from channels.routing import ProtocolTypeRouter, URLRouter 18 | from channels.security.websocket import AllowedHostsOriginValidator 19 | 20 | from conreq.core.websockets.consumers import CommandConsumer 21 | from conreq.utils.environment import get_base_url 22 | 23 | BASE_URL = get_base_url() 24 | if BASE_URL: 25 | BASE_URL = BASE_URL[1:] 26 | 27 | 28 | class LifespanApp: 29 | # pylint: disable=too-few-public-methods 30 | """ 31 | Temporary shim for https://github.com/django/channels/issues/1216 32 | Needed so that hypercorn doesn't display an error. 33 | """ 34 | 35 | def __init__(self, scope): 36 | self.scope = scope 37 | 38 | async def __call__(self, receive, send): 39 | if self.scope["type"] == "lifespan": 40 | while True: 41 | message = await receive() 42 | if message["type"] == "lifespan.startup": 43 | await send({"type": "lifespan.startup.complete"}) 44 | elif message["type"] == "lifespan.shutdown": 45 | await send({"type": "lifespan.shutdown.complete"}) 46 | return 47 | 48 | 49 | application = ProtocolTypeRouter( 50 | { 51 | # ASGI app has concurrency problems, see 52 | # See https://github.com/django/channels/issues/1587 53 | "http": django_asgi_app, 54 | "websocket": AllowedHostsOriginValidator( 55 | AuthMiddlewareStack( 56 | URLRouter([path(BASE_URL, CommandConsumer().as_asgi())]) 57 | ) 58 | ), 59 | "lifespan": LifespanApp, 60 | } 61 | ) 62 | -------------------------------------------------------------------------------- /conreq/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/__init__.py -------------------------------------------------------------------------------- /conreq/core/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/api/__init__.py -------------------------------------------------------------------------------- /conreq/core/api/admin.py: -------------------------------------------------------------------------------- 1 | from rest_framework.authtoken.admin import TokenAdmin 2 | 3 | # Register your models here. 4 | 5 | TokenAdmin.raw_id_fields = ["user"] 6 | -------------------------------------------------------------------------------- /conreq/core/api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "conreq.core.api" 7 | -------------------------------------------------------------------------------- /conreq/core/api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/api/migrations/__init__.py -------------------------------------------------------------------------------- /conreq/core/api/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.db.models.signals import post_save 3 | from django.dispatch import receiver 4 | from rest_framework.authtoken.models import Token 5 | 6 | User = get_user_model() 7 | 8 | # Create your models here. 9 | @receiver(post_save, sender=User) 10 | def create_auth_token( 11 | sender, instance=None, created=False, **kwargs 12 | ): # pylint: disable=unused-argument 13 | # Create the token if it doesn't exist 14 | if not hasattr(instance, "auth_token"): 15 | Token.objects.create(user=instance) 16 | -------------------------------------------------------------------------------- /conreq/core/api/permissions.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | from django.http import HttpRequest 4 | from rest_framework_api_key.models import APIKey 5 | from rest_framework_api_key.permissions import BaseHasAPIKey, KeyParser 6 | 7 | 8 | class HasAPIKey(BaseHasAPIKey): 9 | model = APIKey 10 | key_parser = KeyParser() 11 | 12 | def get_key(self, request: HttpRequest) -> typing.Optional[str]: 13 | # Prefer key in header 14 | header_key = self.key_parser.get(request) 15 | if header_key: 16 | return header_key 17 | # Fallback to key in URL parameters 18 | return request.GET.get("apikey") 19 | -------------------------------------------------------------------------------- /conreq/core/api/serializers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from rest_framework import serializers 3 | 4 | from conreq.core.manage_users.models import Profile 5 | 6 | 7 | class UserProfileSerializer(serializers.ModelSerializer): 8 | class Meta: 9 | model = Profile 10 | fields = ["language", "externally_authenticated"] 11 | 12 | 13 | class UserSerializer(serializers.ModelSerializer): 14 | profile = UserProfileSerializer(required=True) 15 | 16 | class Meta: 17 | model = get_user_model() 18 | fields = [ 19 | "id", 20 | "last_login", 21 | "is_superuser", 22 | "username", 23 | "first_name", 24 | "last_name", 25 | "email", 26 | "is_staff", 27 | "is_active", 28 | "date_joined", 29 | "groups", 30 | "user_permissions", 31 | "profile", 32 | "auth_token", 33 | ] 34 | -------------------------------------------------------------------------------- /conreq/core/arrs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/arrs/__init__.py -------------------------------------------------------------------------------- /conreq/core/arrs/helpers.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | from conreq.core.arrs.sonarr import SonarrManager 4 | 5 | 6 | def wait_for_series_info(tvdb_id, max_retries=15): 7 | """Keeps attempting to fetch a series from Sonarr until it becomes available""" 8 | sonarr_manager = SonarrManager() 9 | series = sonarr_manager.get( 10 | tvdb_id=tvdb_id, obtain_season_info=True, force_update_cache=True 11 | ) 12 | if series is None: 13 | series_fetch_retries = 0 14 | while series is None: 15 | if series_fetch_retries > max_retries: 16 | break 17 | series_fetch_retries = series_fetch_retries + 1 18 | series = sonarr_manager.get( 19 | tvdb_id=tvdb_id, obtain_season_info=True, force_update_cache=True 20 | ) 21 | sleep(0.25) 22 | return series 23 | -------------------------------------------------------------------------------- /conreq/core/arrs/tasks.py: -------------------------------------------------------------------------------- 1 | from huey import crontab 2 | from huey.contrib.djhuey import db_periodic_task 3 | 4 | from conreq.utils.environment import get_str_from_env 5 | 6 | from .radarr import RadarrManager 7 | from .sonarr import SonarrManager 8 | 9 | ARR_REFRESH_INTERNAL = get_str_from_env("ARR_REFRESH_INTERNAL", "*/1") 10 | 11 | 12 | @db_periodic_task(crontab(minute=ARR_REFRESH_INTERNAL, strict=True)) 13 | def refresh_radarr_library(): 14 | """Checks Radarr for new entries.""" 15 | RadarrManager().refresh_library() 16 | 17 | 18 | @db_periodic_task(crontab(minute=ARR_REFRESH_INTERNAL, strict=True)) 19 | def refresh_sonarr_library(): 20 | """Checks Sonarr for new entries.""" 21 | SonarrManager().refresh_library() 22 | -------------------------------------------------------------------------------- /conreq/core/base/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/__init__.py -------------------------------------------------------------------------------- /conreq/core/base/apps.py: -------------------------------------------------------------------------------- 1 | from sqlite3 import Connection 2 | 3 | from django.apps import AppConfig 4 | from django.db.backends.signals import connection_created 5 | 6 | 7 | class BaseConfig(AppConfig): 8 | name = "conreq.core.base" 9 | 10 | 11 | # pylint: disable=unused-argument 12 | @connection_created.connect 13 | def sqlite_connect(sender, connection: Connection, **kwargs): 14 | """Enable integrity constraint with sqlite.""" 15 | if connection.vendor == "sqlite": 16 | cursor = connection.cursor() 17 | cursor.execute("PRAGMA journal_mode = WAL;") 18 | cursor.execute("PRAGMA synchronous = NORMAL;") 19 | cursor.execute("PRAGMA temp_store = MEMORY;") 20 | cursor.execute("PRAGMA foreign_keys = ON;") 21 | 22 | 23 | @connection_created.disconnect 24 | def sqlite_disconnect(sender, connection: Connection, **kwargs): 25 | """Enable integrity constraint with sqlite.""" 26 | if connection.vendor == "sqlite": 27 | cursor = connection.cursor() 28 | cursor.execute("PRAGMA analysis_limit = 400;") 29 | cursor.execute("PRAGMA optimize;") 30 | -------------------------------------------------------------------------------- /conreq/core/base/fields.py: -------------------------------------------------------------------------------- 1 | from django.db.models import URLField 2 | 3 | from . import forms, validators 4 | 5 | 6 | class ExtendedURLField(URLField): 7 | """URL field that supports hostnames (ex. https://sonarr:8000)""" 8 | default_validators = [validators.ExtendedURLValidator()] 9 | 10 | def formfield(self, **kwargs): 11 | # As with CharField, this will cause URL validation to be performed 12 | # twice. 13 | return super().formfield( 14 | **{ 15 | "form_class": forms.ExtendedURLField, 16 | **kwargs, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /conreq/core/base/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth import get_user_model 3 | from django.contrib.auth.forms import UserCreationForm 4 | from django.forms import URLField 5 | 6 | from . import validators 7 | 8 | 9 | class ExtendedURLField(URLField): 10 | """URL field that supports hostnames (ex. https://sonarr:8000)""" 11 | 12 | default_validators = [validators.ExtendedURLValidator()] 13 | 14 | 15 | class InitializationForm(UserCreationForm): 16 | sonarr_url = ExtendedURLField(max_length=255, required=False) 17 | sonarr_api_key = forms.CharField(max_length=255, required=False) 18 | radarr_url = ExtendedURLField(max_length=255, required=False) 19 | radarr_api_key = forms.CharField(max_length=255, required=False) 20 | 21 | class Meta: 22 | model = get_user_model() 23 | fields = ("username", "email", "password1", "password2") 24 | -------------------------------------------------------------------------------- /conreq/core/base/helpers.py: -------------------------------------------------------------------------------- 1 | """Helpers for Base""" 2 | 3 | 4 | def initialize_conreq(conreq_config, form): 5 | """Sets up the initial database values during Conreq's first run sequence.""" 6 | # Obtain the sonarr/radarr parameters 7 | conreq_config.sonarr_url = form.cleaned_data.get("sonarr_url") 8 | conreq_config.sonarr_api_key = form.cleaned_data.get("sonarr_api_key") 9 | conreq_config.radarr_url = form.cleaned_data.get("radarr_url") 10 | conreq_config.radarr_api_key = form.cleaned_data.get("radarr_api_key") 11 | 12 | # Enable Sonarr if URL and API key is configured 13 | if conreq_config.sonarr_url and conreq_config.sonarr_api_key: 14 | conreq_config.sonarr_enabled = True 15 | 16 | # Enable Radarr if URL and API key is configured 17 | if conreq_config.radarr_url and conreq_config.radarr_api_key: 18 | conreq_config.radarr_enabled = True 19 | 20 | # Remember that the database has been initialized 21 | conreq_config.conreq_initialized = True 22 | conreq_config.save() 23 | -------------------------------------------------------------------------------- /conreq/core/base/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/management/__init__.py -------------------------------------------------------------------------------- /conreq/core/base/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/management/commands/__init__.py -------------------------------------------------------------------------------- /conreq/core/base/management/commands/start_conreq_app.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.conf import settings 4 | from django.core.management import call_command 5 | from django.core.management.base import BaseCommand 6 | 7 | APPS_DIR = getattr(settings, "APPS_DIR") 8 | BASE_DIR = getattr(settings, "BASE_DIR") 9 | APP_TEMPLATE_DIR = os.path.join(BASE_DIR, "conreq", "app_template") 10 | 11 | 12 | class Command(BaseCommand): 13 | help = "Creates all files needed to start a new Conreq app." 14 | 15 | def handle(self, *args, **options): 16 | call_command( 17 | "startapp", 18 | "--template=" + APP_TEMPLATE_DIR, 19 | options["name"], 20 | APPS_DIR, 21 | ) 22 | 23 | def add_arguments(self, parser): 24 | parser.add_argument( 25 | "name", 26 | help="Name of the new app.", 27 | ) 28 | -------------------------------------------------------------------------------- /conreq/core/base/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/migrations/__init__.py -------------------------------------------------------------------------------- /conreq/core/base/static/css/dark.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/css/dark.css -------------------------------------------------------------------------------- /conreq/core/base/static/css/initialization.css: -------------------------------------------------------------------------------- 1 | /* Input boxes that gets an error on validation */ 2 | input.invalid { 3 | background-color: #ffdddd; 4 | } 5 | 6 | /* The active form tab */ 7 | .tab { 8 | display: none; 9 | flex-direction: column; 10 | width: 100%; 11 | align-items: center; 12 | justify-content: space-evenly; 13 | } 14 | 15 | /* Current step indicator */ 16 | .step { 17 | height: 15px; 18 | width: 15px; 19 | margin: 0 2px; 20 | background-color: #bbbbbb; 21 | border: none; 22 | border-radius: 50%; 23 | display: inline-block; 24 | opacity: 0.3; 25 | } 26 | 27 | .step.active { 28 | background: var(--accent-color); 29 | opacity: 1; 30 | } 31 | 32 | .step.finish { 33 | background: var(--accent-color); 34 | opacity: 0.5; 35 | } 36 | 37 | /* Navigation controls */ 38 | #prevBtn { 39 | margin-right: 20px; 40 | } 41 | 42 | #box-nav-container { 43 | display: flex; 44 | width: 90%; 45 | justify-content: space-between; 46 | } 47 | -------------------------------------------------------------------------------- /conreq/core/base/static/css/issues.css: -------------------------------------------------------------------------------- 1 | .issue-container { 2 | padding: 15px; 3 | border-radius: 8px; 4 | background: #252525; 5 | color: rgba(255, 255, 255, 0.75); 6 | font-weight: 300; 7 | line-height: 18px; 8 | height: auto; 9 | overflow: hidden; 10 | width: 300px; 11 | } 12 | 13 | .issue-container .content-type-icon { 14 | position: absolute; 15 | top: 10px; 16 | right: 10px; 17 | } 18 | 19 | .issue-container .issue-manage-icons { 20 | position: absolute; 21 | bottom: 10px; 22 | right: 10px; 23 | } 24 | 25 | .issue-container .issue-manage-icons .fas { 26 | transition: 0.3s color ease; 27 | margin-left: 10px; 28 | } 29 | 30 | .issue-container .issue-manage-icons .fas:hover { 31 | color: #fff; 32 | } 33 | 34 | .issue-container .title { 35 | font-weight: 700; 36 | } 37 | 38 | .issue-container .category:not(:last-child) { 39 | margin-bottom: 10px; 40 | } 41 | 42 | .issue-container .category.content-name { 43 | padding-right: 25px; 44 | } 45 | 46 | .issue-container .category.content-name a { 47 | display: block; 48 | } 49 | -------------------------------------------------------------------------------- /conreq/core/base/static/css/login.css: -------------------------------------------------------------------------------- 1 | .sign-in-form { 2 | margin-top: 35px; 3 | } 4 | -------------------------------------------------------------------------------- /conreq/core/base/static/css/navbar.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | background: var(--navbar-bg); 3 | height: var(--navbar-height); 4 | box-shadow: var(--box-shadow); 5 | padding-left: 20px; 6 | padding-right: 20px; 7 | z-index: 3; 8 | display: none; 9 | } 10 | 11 | .navbar-dark .navbar-brand { 12 | margin-left: 20px; 13 | margin-right: 0; 14 | color: rgba(255, 255, 255, 0.5); 15 | font-weight: 500; 16 | font-size: 16px; 17 | } 18 | 19 | .navbar .navbar-toggler { 20 | display: flex; 21 | } 22 | 23 | .navbar-toggler-icon { 24 | font-size: 16px; 25 | } 26 | 27 | .searchbar { 28 | position: relative; 29 | height: 40px; 30 | margin: auto; 31 | margin-bottom: 30px; 32 | width: var(--searchbar-width); 33 | background: rgb(0 0 0 / 0.45); 34 | border-radius: 15px; 35 | transition: background 0.4s ease, color 0.4s ease; 36 | color: rgba(255, 255, 255, 0.3); 37 | overflow: hidden; 38 | display: flex; 39 | } 40 | 41 | .viewport-container .searchbar, 42 | .viewport-container-top .searchbar { 43 | box-shadow: var(--poster-box-shadow); 44 | } 45 | 46 | .searchbar:focus-within { 47 | background: #fff; 48 | color: #252525; 49 | } 50 | 51 | .searchbar input { 52 | box-sizing: border-box; 53 | height: 100%; 54 | max-width: 100%; 55 | background: none; 56 | width: calc(100% - 40px); 57 | color: inherit; 58 | border: none; 59 | font-size: var(--primary-font-size); 60 | font-weight: 500; 61 | padding-left: 15px; 62 | outline: none !important; 63 | } 64 | 65 | .searchbar .fa-search { 66 | top: 0px; 67 | right: 0px; 68 | margin-right: 15px; 69 | width: calc(var(--navbar-height) * 0.3); 70 | color: inherit; 71 | margin: 0 auto; 72 | display: flex; 73 | justify-content: center; 74 | align-items: center; 75 | } 76 | 77 | button { 78 | transition: background 0.3s ease; 79 | } 80 | 81 | button:focus { 82 | outline: none; 83 | background: rgb(255 255 255 / 0.04); 84 | } 85 | -------------------------------------------------------------------------------- /conreq/core/base/static/css/server_settings.css: -------------------------------------------------------------------------------- 1 | .settings-group-title { 2 | font-size: 21px; 3 | line-height: 1.5; 4 | } 5 | 6 | .viewport.settings { 7 | width: 100%; 8 | max-width: calc( 9 | 1920px - var(--sidebar-width) - var(--viewport-total-padding) 10 | ); 11 | } 12 | 13 | .settings-group { 14 | width: 100%; 15 | margin-bottom: 30px; 16 | } 17 | 18 | .settings-input-group { 19 | display: grid; 20 | flex-direction: column; 21 | margin: 0; 22 | grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); 23 | -moz-column-gap: 20px; 24 | column-gap: 50px; 25 | } 26 | 27 | .settings-item-group { 28 | display: flex; 29 | flex-direction: column; 30 | justify-content: center; 31 | min-height: 70px; 32 | } 33 | 34 | .text-input-title { 35 | font-weight: 500; 36 | line-height: 1.5; 37 | } 38 | 39 | .settings-item.text-display-only { 40 | display: flex; 41 | align-items: center; 42 | height: 100%; 43 | padding-left: 15px; 44 | padding-right: 15px; 45 | font-size: 16px; 46 | } 47 | 48 | .settings-item.text-display-only .settings-item-text { 49 | width: calc(100% - 15px); 50 | } 51 | 52 | .settings-item .fas { 53 | margin-left: 15px; 54 | font-size: 18px; 55 | } 56 | 57 | .settings-item .fas:first { 58 | margin-left: auto; 59 | } 60 | 61 | input[type="text"]:not(.content-search) { 62 | width: 100%; 63 | } 64 | 65 | .settings-item.toggler label { 66 | font-weight: 500; 67 | } 68 | 69 | .check-update-btn { 70 | align-self: center; 71 | width: auto; 72 | margin: auto; 73 | } 74 | 75 | .settings-item-details.info { 76 | word-wrap: break-word; 77 | } 78 | 79 | .settings-item-group.toggler { 80 | min-height: 45px; 81 | } 82 | -------------------------------------------------------------------------------- /conreq/core/base/static/css/simple_posters.css: -------------------------------------------------------------------------------- 1 | .viewport .poster-container { 2 | height: var(--poster-height); 3 | } 4 | 5 | .viewport .poster-container .fa-angle-down { 6 | padding-top: 0px; 7 | color: #000000; 8 | background: #e4e4e4; 9 | -webkit-filter: drop-shadow(0 0 6px #000000); 10 | filter: drop-shadow(0 0 6px #000000); 11 | border-radius: 20px; 12 | width: 20px; 13 | height: 20px; 14 | left: 50%; 15 | bottom: 3px; 16 | transform: translate(-50%, 0%); 17 | line-height: 1.15; 18 | opacity: 0.4; 19 | transition: color 0.3s ease, background 0.3s ease, opacity 0.3s ease; 20 | } 21 | 22 | .viewport .poster-container:hover .fa-angle-down { 23 | opacity: 1; 24 | } 25 | 26 | .viewport .poster-container .fa-angle-down:hover { 27 | color: var(--accent-color); 28 | background: #ffffff; 29 | } 30 | 31 | .viewport .poster-container > a > .requested-by { 32 | top: calc(var(--poster-height) - 55px); 33 | left: 5px; 34 | right: unset; 35 | opacity: 1; 36 | } 37 | 38 | .viewport .poster-container > a > .requested-by { 39 | max-width: calc(100% - 10px); 40 | } 41 | 42 | .viewport .poster-container .poster-description-container { 43 | opacity: 0.85; 44 | position: absolute; 45 | top: -2px; 46 | left: -2px; 47 | width: calc(var(--poster-width) + 4px); 48 | pointer-events: none; 49 | background: linear-gradient(180deg, black, transparent); 50 | border: none; 51 | transition: opacity 0.6s ease; 52 | height: -webkit-fit-content; 53 | height: -moz-fit-content; 54 | height: fit-content; 55 | padding-top: 2px; 56 | padding-bottom: 25px; 57 | color: #d8d8d8; 58 | text-shadow: -2px -2px 6px #000, 2px -2px 6px #000, -2px 2px 6px #000, 59 | 2px 2px 6px #000; 60 | } 61 | 62 | .availablity-dot { 63 | margin: 8px 7px; 64 | } 65 | 66 | .viewport .poster-container:hover .poster-description-container { 67 | opacity: 1; 68 | } 69 | 70 | .poster-container.person-roles .poster-description, 71 | .poster-container.cast .poster-description { 72 | color: inherit; 73 | font-weight: 400; 74 | } 75 | 76 | .poster-container.tv .poster-description, 77 | .poster-container.movie .poster-description { 78 | display: none; 79 | } 80 | 81 | .viewport .poster-container .poster { 82 | transform: scale(1.08); 83 | } 84 | 85 | .viewport .poster-container .poster:hover { 86 | transform: none; 87 | } 88 | 89 | .viewport .poster-container > a > .content-type-icon { 90 | line-height: 1; 91 | top: unset; 92 | bottom: 5px; 93 | left: 5px; 94 | } 95 | 96 | .viewport .poster-container .description-tint { 97 | display: none; 98 | } 99 | -------------------------------------------------------------------------------- /conreq/core/base/static/css/viewport.css: -------------------------------------------------------------------------------- 1 | .viewport-container, 2 | .viewport-container-top { 3 | position: absolute; 4 | height: calc(100% - var(--navbar-height)); 5 | overflow-x: hidden; 6 | overflow-y: auto; 7 | overflow-y: overlay; 8 | width: calc(100vw - var(--viewport-left)); 9 | padding: var(--viewport-padding); 10 | padding-bottom: 0; 11 | display: inline-block; 12 | color: rgb(255 255 255 / 0.75); 13 | } 14 | 15 | .viewport { 16 | padding-bottom: var(--viewport-padding); 17 | min-height: -webkit-fit-content; 18 | min-height: -moz-fit-content; 19 | min-height: fit-content; 20 | margin: auto; 21 | transition: opacity 1.5s ease; 22 | } 23 | 24 | .empty-viewport { 25 | text-align: center; 26 | min-width: -webkit-fit-content; 27 | min-width: -moz-fit-content; 28 | min-width: fit-content; 29 | width: 30%; 30 | max-width: 100%; 31 | margin: auto; 32 | margin-top: 10px; 33 | padding: 40px; 34 | border-radius: 6px; 35 | box-shadow: var(--box-shadow); 36 | line-height: 1.5; 37 | } 38 | 39 | .viewport-container > .loading-animation-container, 40 | .viewport-container-top > .loading-animation-container { 41 | display: flex; 42 | height: 100%; 43 | width: 100%; 44 | } 45 | -------------------------------------------------------------------------------- /conreq/core/base/static/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-114x114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-114x114-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-120x120-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-120x120-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-144x144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-144x144-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-152x152-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-152x152-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-180x180-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-180x180-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-57x57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-57x57-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-60x60-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-60x60-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-72x72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-72x72-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-76x76-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-76x76-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | #31b58f 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /conreq/core/base/static/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/favicon-16x16.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/favicon-32x32.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/favicon.ico -------------------------------------------------------------------------------- /conreq/core/base/static/icons/maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/maskable.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/mstile-150x150.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/mstile-310x310.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/mstile-70x70.png -------------------------------------------------------------------------------- /conreq/core/base/static/icons/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/static/icons/android-chrome-192x192.png?v=A053y77goM", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/static/icons/android-chrome-512x512.png?v=A053y77goM", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#04110d", 17 | "background_color": "#04110d", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /conreq/core/base/static/icons/standard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/icons/standard.png -------------------------------------------------------------------------------- /conreq/core/base/static/images/conreq_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/images/conreq_logo.png -------------------------------------------------------------------------------- /conreq/core/base/static/images/conreq_logo_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/images/conreq_logo_dark.png -------------------------------------------------------------------------------- /conreq/core/base/static/images/person_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/images/person_placeholder.png -------------------------------------------------------------------------------- /conreq/core/base/static/images/poster_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/images/poster_placeholder.png -------------------------------------------------------------------------------- /conreq/core/base/static/images/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/static/images/transparent.png -------------------------------------------------------------------------------- /conreq/core/base/static/js/modal.js: -------------------------------------------------------------------------------- 1 | let modal_loaded = false; 2 | 3 | // Fetches a modal via AJAX 4 | var generate_modal = async function (modal_url) { 5 | let modal_dialog = $("#modal-dialog"); 6 | // Fetch the series modal 7 | modal_loaded = false; 8 | get_url(modal_url, async function (modal_html) { 9 | // Save that the modal was successfully loaded 10 | modal_loaded = true; 11 | 12 | // Place the new HTML on the page 13 | modal_dialog[0].innerHTML = DOMPurify.sanitize(modal_html); 14 | 15 | // Show the new content 16 | $("#modal-container .loading-animation").hide(); 17 | $("#modal-content").show(); 18 | $("#modal-container").modal("show"); 19 | 20 | // Add click events 21 | request_btn_click_event(); 22 | content_modal_click_event(); 23 | modal_select_all_btn_click_event(); 24 | modal_expand_btn_click_event(); 25 | row_title_click_event(); 26 | row_checkbox_click_event(); 27 | row_suboption_title_click_event(); 28 | row_suboption_checkbox_click_event(); 29 | create_report_modal_click_event(); 30 | report_btn_click_event(); 31 | simple_filter_btn_click_event(); 32 | modal_poster_popup_click_event(); 33 | delete_user_btn_click_event(); 34 | save_user_btn_click_event(); 35 | }).fail(async function () { 36 | // Server couldn't fetch the modal 37 | if (http_request.statusText != "abort") { 38 | conreq_no_response_toast_message(); 39 | } 40 | $("#modal-container").modal("hide"); 41 | }); 42 | 43 | // If the modal is taking too long to load, show a loading animation 44 | setTimeout(async function () { 45 | if (!modal_loaded) { 46 | // Show the loading icon 47 | $("#modal-content").hide(); 48 | $("#modal-container").modal("show"); 49 | $("#modal-container .loading-animation").show(); 50 | } 51 | }, 300); 52 | }; 53 | -------------------------------------------------------------------------------- /conreq/core/base/static/js/sidebar.js: -------------------------------------------------------------------------------- 1 | $(document).ready(async function () { 2 | $(".sidebar").trigger("prepare"); 3 | new SimpleBar($("#sidebar")[0]); 4 | $(".sidebar").trigger("loaded"); 5 | }); 6 | -------------------------------------------------------------------------------- /conreq/core/base/static/js/sign_up.js: -------------------------------------------------------------------------------- 1 | document.querySelector("form").action = 2 | document.querySelector("form").action + window.location.search; 3 | -------------------------------------------------------------------------------- /conreq/core/base/tasks.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sqlite3 3 | 4 | from django.conf import settings 5 | from django.db import connection 6 | from huey import crontab 7 | from huey.contrib.djhuey import db_periodic_task 8 | 9 | from conreq.utils.environment import get_database_type 10 | 11 | DB_ENGINE = get_database_type() 12 | HUEY_FILENAME = getattr(settings, "HUEY_FILENAME") 13 | 14 | 15 | @db_periodic_task(crontab(minute="0", hour="0", strict=True)) 16 | def vacuum_huey_sqlite_db(): 17 | """Periodically preforms a SQLITE vacuum on the background task database.""" 18 | with sqlite3.connect(HUEY_FILENAME) as cursor: 19 | cursor.execute( 20 | # Only keep the 1000 latest tasks 21 | """DELETE FROM task 22 | WHERE id NOT IN ( 23 | SELECT id 24 | FROM ( 25 | SELECT id 26 | FROM task 27 | ORDER BY id DESC 28 | LIMIT 1000 29 | ) foo 30 | ); 31 | """ 32 | ) 33 | with sqlite3.connect(HUEY_FILENAME) as cursor: 34 | cursor.execute("VACUUM") 35 | 36 | 37 | if DB_ENGINE == "SQLITE3": 38 | 39 | @db_periodic_task(crontab(minute="0", hour="0", strict=True)) 40 | def vacuum_conreq_sqlite_db(): 41 | """Periodically performs any cleanup tasks needed for the default database.""" 42 | with connection.cursor() as cursor: 43 | cursor.execute("VACUUM") 44 | -------------------------------------------------------------------------------- /conreq/core/base/templates/primary/base_app.html: -------------------------------------------------------------------------------- 1 | {% load solo_tags %} 2 | {% get_solo 'server_settings.ConreqConfig' as server_settings %} 3 | {% load static compress %} 4 | {% load static %} 5 | {% load conreq_tags %} 6 | 7 | 8 | 9 | 10 | {% include "primary/head_content.html" %} 11 | 12 | 13 | 14 | 15 | 16 | {% include "primary/navbar.html" %} 17 | {% include "primary/sidebar.html" %} 18 | {% include "modal/default.html" %} 19 |
20 | {% include "primary/loading_animation_container.html" %} 21 |
22 |
23 | {% include "primary/loading_animation_container.html" %} 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /conreq/core/base/templates/primary/head_content_slim.html: -------------------------------------------------------------------------------- 1 | {% load static compress %} 2 | {% load solo_tags %} 3 | {% get_solo 'server_settings.ConreqConfig' as server_settings %} 4 | {% load static %} 5 | {% load conreq_tags %} 6 | 7 | 8 | {% app_name %} 9 | 10 | 11 | 12 | 13 | {% include "primary/icons.html" %} 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 25 | 27 | 28 | 29 | 32 | 33 | {% compress css %} 34 | 35 | 36 | 37 | 38 | 39 | 40 | {% endcompress %} 41 | 42 | {% compress js %} 43 | 44 | 45 | 46 | 47 | 48 | {% endcompress %} -------------------------------------------------------------------------------- /conreq/core/base/templates/primary/icons.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /conreq/core/base/templates/primary/loading_animation.html: -------------------------------------------------------------------------------- 1 |
2 | Loading... 3 |
-------------------------------------------------------------------------------- /conreq/core/base/templates/primary/loading_animation_container.html: -------------------------------------------------------------------------------- 1 |
2 | {% include "primary/loading_animation.html" %} 3 |
-------------------------------------------------------------------------------- /conreq/core/base/templates/primary/navbar.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load conreq_tags %} 3 | -------------------------------------------------------------------------------- /conreq/core/base/templates/primary/searchbar.html: -------------------------------------------------------------------------------- 1 | {% load conreq_tags %} 2 | 3 | -------------------------------------------------------------------------------- /conreq/core/base/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/base/templatetags/__init__.py -------------------------------------------------------------------------------- /conreq/core/base/templatetags/conreq_tags.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.urls import reverse 3 | 4 | from conreq.utils.environment import get_base_url, get_str_from_env 5 | 6 | register = template.Library() 7 | base_url_len = len(get_base_url()) 8 | conreq_app_name = get_str_from_env("APP_NAME", "Conreq") 9 | conreq_app_description = get_str_from_env("APP_DESCRIPTION", "Content Requesting") 10 | 11 | 12 | @register.simple_tag 13 | def viewport_url(namespace): 14 | url = reverse(namespace) 15 | return "#" + url[base_url_len:] 16 | 17 | 18 | @register.simple_tag 19 | def viewport_top_url(namespace): 20 | url = reverse(namespace) 21 | return "#" + "/display" + url[base_url_len:] 22 | 23 | 24 | @register.simple_tag 25 | def app_name(): 26 | return conreq_app_name 27 | 28 | 29 | @register.simple_tag 30 | def app_description(): 31 | return conreq_app_description 32 | 33 | 34 | @register.filter 35 | def contains_valid_id(results): 36 | if isinstance(results, list): 37 | for result in results: 38 | if isinstance(result, dict) and result.get("conreq_valid_id"): 39 | return True 40 | return False 41 | 42 | 43 | @register.filter 44 | def email_enabled(_): 45 | """Temporary hacky shortcut to determine if email is enabled.""" 46 | from django.conf import settings 47 | 48 | return settings.EMAIL_ENABLED 49 | -------------------------------------------------------------------------------- /conreq/core/base/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "base" 6 | 7 | urlpatterns = [ 8 | path("", views.landing, name="landing"), 9 | path("home/", views.home, name="homescreen"), 10 | ] 11 | -------------------------------------------------------------------------------- /conreq/core/base/validators.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django.core.validators import URLValidator 4 | from django.utils.deconstruct import deconstructible 5 | from django.utils.regex_helper import _lazy_re_compile 6 | 7 | 8 | @deconstructible 9 | class ExtendedURLValidator(URLValidator): 10 | """URL validator that supports hostnames (ex. https://sonarr:8000)""" 11 | 12 | # pylint: disable=too-few-public-methods 13 | ul = URLValidator.ul 14 | ipv4_re = URLValidator.ipv4_re 15 | ipv6_re = URLValidator.ipv6_re 16 | hostname_re = URLValidator.hostname_re 17 | domain_re = URLValidator.domain_re 18 | 19 | tld_re = ( 20 | r"\.?" # OPTIONAL dot (allows for hostnames) 21 | r"(?!-)" # can't start with a dash 22 | r"(?:[a-z" + ul + "-]{2,63}" # domain label 23 | r"|xn--[a-z0-9]{1,59})" # or punycode label 24 | r"(? 3 | 4 | 5 | 6 | {% include "primary/head_content_slim.html" %} 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 |

RESET MY PASSWORD

23 |

Enter your username or email.

24 | 25 |
26 | {% csrf_token %} 27 | {{ form.email }} 28 | 29 |
30 | 31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /conreq/core/password_reset/templates/conreq/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | {% include "primary/head_content_slim.html" %} 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 | {% if validlink %} 23 |

PASSWORD RESET

24 |

Please enter your new password.

25 |
26 | {% csrf_token %} 27 | {{ form.new_password1.errors }} 28 | {{ form.new_password1 }} 29 | {{ form.new_password2.errors }} 30 | {{ form.new_password2 }} 31 | 32 |
33 | {% else %} 34 |

INVALID PASSWORD RESET

35 |

Key used or expired.

36 |

37 | The password reset link was invalid, possibly because it has already been used. Please request a new 38 | password reset. 39 |

40 | {% endif %} 41 | 42 |
43 |
44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /conreq/core/password_reset/templates/conreq/password_reset_sent.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | {% include "primary/head_content_slim.html" %} 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |

PASSWORD RESET SENT

24 |

Reset email has been sent.

25 |

26 | We’ve emailed you instructions for setting your password, if an account exists with the details you 27 | entered. You 28 | should receive them shortly. 29 |

30 |

31 | If you don’t receive an email, please make sure you’ve entered the correct information. 32 | Additionally, make sure to check your spam folder. 33 |

34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /conreq/core/password_reset/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from .views import PasswordResetView, PasswordResetConfirmView, PassWordResetDoneView 4 | 5 | urlpatterns = [ 6 | path("", PasswordResetView.as_view(), name="password_reset"), 7 | path("sent", PassWordResetDoneView.as_view(), name="password_reset_sent"), 8 | path( 9 | "/", 10 | PasswordResetConfirmView.as_view(), 11 | name="password_reset_confirm", 12 | ), 13 | ] 14 | -------------------------------------------------------------------------------- /conreq/core/password_reset/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import views as auth_views 2 | from django.urls import reverse_lazy 3 | 4 | from conreq.core.password_reset.forms import PasswordResetForm, SetPasswordForm 5 | 6 | 7 | class PasswordResetView(auth_views.PasswordResetView): 8 | success_url = reverse_lazy("password_reset_sent") 9 | template_name = "conreq/password_reset.html" 10 | form_class = PasswordResetForm 11 | 12 | 13 | class PassWordResetDoneView(auth_views.PasswordResetDoneView): 14 | template_name = "conreq/password_reset_sent.html" 15 | 16 | 17 | class PasswordResetConfirmView(auth_views.PasswordResetConfirmView): 18 | template_name = "conreq/password_reset_confirm.html" 19 | success_url = reverse_lazy("base:homescreen") 20 | post_reset_login = True 21 | form_class = SetPasswordForm 22 | -------------------------------------------------------------------------------- /conreq/core/pwa/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License (MIT) 2 | 3 | Copyright (c) 2014 Scott Vitale, Silvio Luis and Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /conreq/core/pwa/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/pwa/__init__.py -------------------------------------------------------------------------------- /conreq/core/pwa/app_settings.py: -------------------------------------------------------------------------------- 1 | """ Settings required by django-app. """ 2 | import os 3 | 4 | from django.conf import settings 5 | from django.shortcuts import resolve_url 6 | from django.urls import get_script_prefix 7 | from django.utils.functional import lazy 8 | 9 | from conreq.utils.environment import get_base_url 10 | 11 | # Lazy-evaluate URLs so including pwa.urls in root urlconf works 12 | resolve_url = lazy(resolve_url, str) 13 | 14 | # Get script prefix for apps not mounted under / 15 | _PWA_SCRIPT_PREFIX = get_script_prefix() 16 | 17 | # Path to the service worker implementation. Default implementation is empty. 18 | PWA_SERVICE_WORKER_PATH = getattr( 19 | settings, 20 | "PWA_SERVICE_WORKER_PATH", 21 | os.path.join( 22 | os.path.abspath(os.path.dirname(__file__)), "templates", "serviceworker.js" 23 | ), 24 | ) 25 | # App parameters to include in manifest.json and appropriate meta tags 26 | PWA_APP_NAME = getattr(settings, "PWA_APP_NAME", "MyApp") 27 | PWA_APP_DESCRIPTION = getattr(settings, "PWA_APP_DESCRIPTION", "My Progressive Web App") 28 | PWA_APP_ROOT_URL = resolve_url( 29 | getattr(settings, "PWA_APP_ROOT_URL", _PWA_SCRIPT_PREFIX) 30 | ) 31 | PWA_APP_THEME_COLOR = getattr(settings, "PWA_APP_THEME_COLOR", "#000") 32 | PWA_APP_BACKGROUND_COLOR = getattr(settings, "PWA_APP_BACKGROUND_COLOR", "#fff") 33 | PWA_APP_DISPLAY = getattr(settings, "PWA_APP_DISPLAY", "standalone") 34 | PWA_APP_SCOPE = resolve_url(getattr(settings, "PWA_APP_SCOPE", _PWA_SCRIPT_PREFIX)) 35 | PWA_APP_DEBUG_MODE = getattr(settings, "PWA_APP_DEBUG_MODE", True) 36 | PWA_APP_ORIENTATION = getattr(settings, "PWA_APP_ORIENTATION", "any") 37 | PWA_APP_START_URL = resolve_url( 38 | getattr(settings, "PWA_APP_START_URL", _PWA_SCRIPT_PREFIX) 39 | ) 40 | PWA_APP_FETCH_URL = resolve_url( 41 | getattr(settings, "PWA_APP_FETCH_URL", _PWA_SCRIPT_PREFIX) 42 | ) 43 | PWA_APP_STATUS_BAR_COLOR = getattr(settings, "PWA_APP_STATUS_BAR_COLOR", "default") 44 | PWA_APP_ICONS = getattr(settings, "PWA_APP_ICONS", []) 45 | PWA_APP_ICONS_APPLE = getattr(settings, "PWA_APP_ICONS_APPLE", []) 46 | PWA_APP_SPLASH_SCREEN = getattr(settings, "PWA_APP_SPLASH_SCREEN", []) 47 | PWA_APP_DIR = getattr(settings, "PWA_APP_DIR", "auto") 48 | PWA_APP_LANG = getattr(settings, "PWA_APP_LANG", "en-US") 49 | PWA_BASE_URL = get_base_url() 50 | -------------------------------------------------------------------------------- /conreq/core/pwa/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PwaConfig(AppConfig): 5 | name = "conreq.core.pwa" 6 | -------------------------------------------------------------------------------- /conreq/core/pwa/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/pwa/migrations/__init__.py -------------------------------------------------------------------------------- /conreq/core/pwa/templates/manifest.json: -------------------------------------------------------------------------------- 1 | {% load pwa %} 2 | { 3 | "name": {{ PWA_APP_NAME|js }}, 4 | "short_name": {{ PWA_APP_NAME|js }}, 5 | "description": {{ PWA_APP_DESCRIPTION|js }}, 6 | "start_url": {{ PWA_APP_START_URL|js }}, 7 | "display": {{ PWA_APP_DISPLAY|js }}, 8 | "scope": {{ PWA_APP_SCOPE|js }}, 9 | "orientation": {{ PWA_APP_ORIENTATION|js }}, 10 | "background_color": {{ PWA_APP_BACKGROUND_COLOR|js }}, 11 | "theme_color": {{ PWA_APP_THEME_COLOR|js }}, 12 | "status_bar": {{ PWA_APP_STATUS_BAR_COLOR|js }}, 13 | "icons": {{ PWA_APP_ICONS|js }}, 14 | "dir": {{ PWA_APP_DIR|js }}, 15 | "lang": {{ PWA_APP_LANG|js }} 16 | } 17 | -------------------------------------------------------------------------------- /conreq/core/pwa/templates/offline.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load conreq_tags %} 3 | 4 | 5 | 6 | 7 | {% include "primary/head_content_slim.html" %} 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |

Could not connect to {% app_name %}.

20 |

Check your network connection or try again later.

21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /conreq/core/pwa/templates/pwa.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% for icon in PWA_APP_ICONS_APPLE %} 19 | 20 | {% endfor %} 21 | {% for icon in PWA_APP_ICONS %} 22 | 23 | {% endfor %} 24 | 25 | 26 | {% for splash in PWA_APP_SPLASH_SCREEN %} 27 | 28 | {% endfor %} 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /conreq/core/pwa/templates/serviceworker.js: -------------------------------------------------------------------------------- 1 | // Base Service Worker implementation. To use your own Service Worker, set the PWA_SERVICE_WORKER_PATH variable in settings.py 2 | 3 | var staticCacheName = "django-pwa-v" + new Date().getTime(); 4 | var filesToCache = [ 5 | "{{ base_url|escapejs }}/offline/", 6 | "{{ base_url|escapejs }}/static/css/main_slim.css", 7 | ]; 8 | 9 | // Cache on install 10 | self.addEventListener("install", (event) => { 11 | this.skipWaiting(); 12 | event.waitUntil( 13 | caches.open(staticCacheName).then((cache) => { 14 | return cache.addAll(filesToCache); 15 | }) 16 | ); 17 | }); 18 | 19 | // Clear cache on activate 20 | self.addEventListener("activate", (event) => { 21 | event.waitUntil( 22 | caches.keys().then((cacheNames) => { 23 | return Promise.all( 24 | cacheNames 25 | .filter((cacheName) => cacheName.startsWith("django-pwa-")) 26 | .filter((cacheName) => cacheName !== staticCacheName) 27 | .map((cacheName) => caches.delete(cacheName)) 28 | ); 29 | }) 30 | ); 31 | }); 32 | 33 | // Serve from Cache 34 | self.addEventListener("fetch", (event) => { 35 | event.respondWith( 36 | caches 37 | .match(event.request) 38 | .then((response) => { 39 | return response || fetch(event.request); 40 | }) 41 | .catch(() => { 42 | return caches.match("{{ base_url|escapejs }}/offline/"); 43 | }) 44 | ); 45 | }); 46 | -------------------------------------------------------------------------------- /conreq/core/pwa/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/pwa/templatetags/__init__.py -------------------------------------------------------------------------------- /conreq/core/pwa/templatetags/pwa.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django import template 4 | from django.core.serializers.json import DjangoJSONEncoder 5 | from django.utils.safestring import mark_safe 6 | 7 | from .. import app_settings 8 | 9 | register = template.Library() 10 | 11 | 12 | @register.filter(is_safe=True) 13 | def js(obj): 14 | """Transform a python object so it can be safely used in javascript/JSON.""" 15 | return mark_safe(json.dumps(obj, cls=DjangoJSONEncoder)) 16 | 17 | 18 | @register.inclusion_tag("pwa.html", takes_context=True) 19 | def progressive_web_app_meta(context): 20 | # Pass all PWA_* settings into the template 21 | return { 22 | setting_name: getattr(app_settings, setting_name) 23 | for setting_name in dir(app_settings) 24 | if setting_name.startswith("PWA_") 25 | } 26 | -------------------------------------------------------------------------------- /conreq/core/pwa/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from .views import manifest, offline, service_worker 4 | 5 | app_name = "pwa" 6 | # Serve up serviceworker.js and manifest.json at the root 7 | urlpatterns = [ 8 | path("serviceworker.js", service_worker, name="serviceworker"), 9 | path("manifest.json", manifest, name="manifest"), 10 | path("offline/", offline, name="offline"), 11 | ] 12 | -------------------------------------------------------------------------------- /conreq/core/pwa/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | from conreq.utils.environment import get_base_url 4 | 5 | from . import app_settings 6 | 7 | BASE_URL = get_base_url() 8 | 9 | 10 | def service_worker(request): 11 | return render( 12 | request, 13 | "serviceworker.js", 14 | {"base_url": BASE_URL}, 15 | content_type="application/javascript", 16 | ) 17 | 18 | 19 | def manifest(request): 20 | return render( 21 | request, 22 | "manifest.json", 23 | { 24 | setting_name: getattr(app_settings, setting_name) 25 | for setting_name in dir(app_settings) 26 | if setting_name.startswith("PWA_") 27 | }, 28 | content_type="application/json", 29 | ) 30 | 31 | 32 | def offline(request): 33 | return render(request, "offline.html") 34 | -------------------------------------------------------------------------------- /conreq/core/search/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/search/__init__.py -------------------------------------------------------------------------------- /conreq/core/search/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SearchConfig(AppConfig): 5 | name = "conreq.core.search" 6 | -------------------------------------------------------------------------------- /conreq/core/search/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/search/migrations/__init__.py -------------------------------------------------------------------------------- /conreq/core/search/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "search" 6 | urlpatterns = [ 7 | path("", views.search, name="main"), 8 | ] 9 | -------------------------------------------------------------------------------- /conreq/core/search/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.decorators import login_required 2 | from django.http import HttpResponse 3 | from django.template import loader 4 | from django.views.decorators.cache import cache_page 5 | 6 | from conreq.core.discover.helpers import set_many_availability 7 | from conreq.core.tmdb.discovery import TmdbDiscovery 8 | from conreq.core.tmdb.search import TmdbSearch 9 | from conreq.utils.debug import performance_metrics 10 | 11 | 12 | @cache_page(15) 13 | @login_required 14 | @performance_metrics() 15 | def search(request): 16 | content_discovery = TmdbDiscovery() 17 | searcher = TmdbSearch() 18 | 19 | # Get the ID from the URL 20 | query = request.GET.get("query", "") 21 | content_type = request.GET.get("content_type", None) 22 | page = int(request.GET.get("page", 1)) 23 | 24 | # Determine which search method to use (tv/movie/all) 25 | if content_type == "tv": 26 | tmdb_results = searcher.television(query, page) 27 | elif content_type == "movie": 28 | tmdb_results = searcher.movie(query, page) 29 | else: 30 | tmdb_results = searcher.all(query, page) 31 | 32 | if tmdb_results: 33 | tmdb_results = tmdb_results.get("results") 34 | 35 | # Determine the availability 36 | content_discovery.determine_id_validity(tmdb_results) 37 | set_many_availability(tmdb_results) 38 | 39 | context = { 40 | "all_cards": tmdb_results, 41 | "content_type": content_type, 42 | "search_query": query, 43 | } 44 | template = loader.get_template("viewport/search.html") 45 | return HttpResponse(template.render(context, request)) 46 | -------------------------------------------------------------------------------- /conreq/core/server_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/server_settings/__init__.py -------------------------------------------------------------------------------- /conreq/core/server_settings/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from conreq.core.server_settings.models import ConreqConfig 4 | 5 | 6 | # Register your models here. 7 | @admin.register(ConreqConfig) 8 | class ServerSettings(admin.ModelAdmin): 9 | pass 10 | -------------------------------------------------------------------------------- /conreq/core/server_settings/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ServerSettingsConfig(AppConfig): 5 | name = "conreq.core.server_settings" 6 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0002_auto_20210118_2004.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-19 04:04 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='conreqconfig', 15 | name='radarr_anime_folder', 16 | field=models.PositiveIntegerField(default=1), 17 | ), 18 | migrations.AlterField( 19 | model_name='conreqconfig', 20 | name='radarr_movies_folder', 21 | field=models.PositiveIntegerField(default=1), 22 | ), 23 | migrations.AlterField( 24 | model_name='conreqconfig', 25 | name='sonarr_anime_folder', 26 | field=models.PositiveIntegerField(default=1), 27 | ), 28 | migrations.AlterField( 29 | model_name='conreqconfig', 30 | name='sonarr_tv_folder', 31 | field=models.PositiveIntegerField(default=1), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0003_conreqconfig_conreq_initialized.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-19 08:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0002_auto_20210118_2004'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='conreqconfig', 15 | name='conreq_initialized', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0005_auto_20210127_0107.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-27 09:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0004_auto_20210119_1742'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='conreqconfig', 15 | name='conreq_dark_theme', 16 | field=models.BooleanField(default=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0006_auto_20210130_0236.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-30 10:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0005_auto_20210127_0107'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='conreqconfig', 15 | name='conreq_custom_css', 16 | field=models.CharField(blank=True, default='', max_length=100), 17 | ), 18 | migrations.AlterField( 19 | model_name='conreqconfig', 20 | name='conreq_custom_js', 21 | field=models.CharField(blank=True, default='', max_length=100), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0007_conreqconfig_conreq_http_header_auth.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-02-02 12:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0006_auto_20210130_0236'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='conreqconfig', 15 | name='conreq_http_header_auth', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0008_auto_20210203_0053.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-02-03 08:53 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0007_conreqconfig_conreq_http_header_auth'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='conreqconfig', 15 | name='conreq_simple_posters', 16 | field=models.BooleanField(default=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0009_auto_20210204_1938.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-02-05 03:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0008_auto_20210203_0053'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='conreqconfig', 15 | name='conreq_app_url', 16 | field=models.CharField(blank=True, default='', max_length=255), 17 | ), 18 | migrations.AlterField( 19 | model_name='conreqconfig', 20 | name='email_smtp_server', 21 | field=models.CharField(blank=True, default='', max_length=255), 22 | ), 23 | migrations.AlterField( 24 | model_name='conreqconfig', 25 | name='radarr_url', 26 | field=models.CharField(blank=True, default='', max_length=255), 27 | ), 28 | migrations.AlterField( 29 | model_name='conreqconfig', 30 | name='sonarr_url', 31 | field=models.CharField(blank=True, default='', max_length=255), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0010_auto_20210205_2326.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-02-06 07:26 2 | 3 | import conreq.core.base.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("server_settings", "0009_auto_20210204_1938"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="conreqconfig", 16 | name="conreq_app_url", 17 | field=models.URLField(blank=True, default=""), 18 | ), 19 | migrations.AlterField( 20 | model_name="conreqconfig", 21 | name="radarr_url", 22 | field=conreq.core.base.fields.ExtendedURLField(blank=True, default=""), 23 | ), 24 | migrations.AlterField( 25 | model_name="conreqconfig", 26 | name="sonarr_url", 27 | field=conreq.core.base.fields.ExtendedURLField(blank=True, default=""), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0011_auto_20210205_2343.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-02-06 07:43 2 | 3 | from django.db import migrations 4 | import url_or_relative_url_field.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('server_settings', '0010_auto_20210205_2326'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='conreqconfig', 16 | name='conreq_custom_css', 17 | field=url_or_relative_url_field.fields.URLOrRelativeURLField(blank=True, default=''), 18 | ), 19 | migrations.AlterField( 20 | model_name='conreqconfig', 21 | name='conreq_custom_js', 22 | field=url_or_relative_url_field.fields.URLOrRelativeURLField(blank=True, default=''), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0012_auto_20210209_2144.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-02-10 05:44 2 | 3 | from django.db import migrations, models 4 | import encrypted_fields.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('server_settings', '0011_auto_20210205_2343'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='conreqconfig', 16 | name='conreq_api_key', 17 | field=models.CharField(blank=True, default='', max_length=255), 18 | ), 19 | migrations.AlterField( 20 | model_name='conreqconfig', 21 | name='email_password', 22 | field=encrypted_fields.fields.EncryptedCharField(blank=True, default='', max_length=255), 23 | ), 24 | migrations.AlterField( 25 | model_name='conreqconfig', 26 | name='email_username', 27 | field=encrypted_fields.fields.EncryptedCharField(blank=True, default='', max_length=255), 28 | ), 29 | migrations.AlterField( 30 | model_name='conreqconfig', 31 | name='radarr_api_key', 32 | field=models.CharField(blank=True, default='', max_length=255), 33 | ), 34 | migrations.AlterField( 35 | model_name='conreqconfig', 36 | name='sonarr_api_key', 37 | field=models.CharField(blank=True, default='', max_length=255), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0013_conreqconfig_conreq_allow_tv_specials.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-03-13 09:54 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0012_auto_20210209_2144'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='conreqconfig', 15 | name='conreq_allow_tv_specials', 16 | field=models.BooleanField(default=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0014_auto_20210313_0231.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-03-13 10:31 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0013_conreqconfig_conreq_allow_tv_specials'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='conreqconfig', 15 | name='conreq_app_logo', 16 | ), 17 | migrations.RemoveField( 18 | model_name='conreqconfig', 19 | name='conreq_app_url', 20 | ), 21 | migrations.RemoveField( 22 | model_name='conreqconfig', 23 | name='conreq_guest_login', 24 | ), 25 | migrations.RemoveField( 26 | model_name='conreqconfig', 27 | name='conreq_language', 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0015_remove_conreqconfig_conreq_app_name.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-03-21 09:02 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0014_auto_20210313_0231'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='conreqconfig', 15 | name='conreq_app_name', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0016_alter_conreqconfig_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-04-13 05:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0015_remove_conreqconfig_conreq_app_name'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='conreqconfig', 15 | name='id', 16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0017_remove_conreqconfig_conreq_dark_theme.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-04-26 04:01 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0016_alter_conreqconfig_id'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='conreqconfig', 15 | name='conreq_dark_theme', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0018_auto_20210428_2039.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-04-29 03:39 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0017_remove_conreqconfig_conreq_dark_theme'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='conreqconfig', 15 | name='radarr_anime_folder', 16 | field=models.PositiveIntegerField(default=0), 17 | ), 18 | migrations.AlterField( 19 | model_name='conreqconfig', 20 | name='radarr_anime_quality_profile', 21 | field=models.PositiveIntegerField(default=0), 22 | ), 23 | migrations.AlterField( 24 | model_name='conreqconfig', 25 | name='radarr_movies_folder', 26 | field=models.PositiveIntegerField(default=0), 27 | ), 28 | migrations.AlterField( 29 | model_name='conreqconfig', 30 | name='radarr_movies_quality_profile', 31 | field=models.PositiveIntegerField(default=0), 32 | ), 33 | migrations.AlterField( 34 | model_name='conreqconfig', 35 | name='sonarr_anime_folder', 36 | field=models.PositiveIntegerField(default=0), 37 | ), 38 | migrations.AlterField( 39 | model_name='conreqconfig', 40 | name='sonarr_anime_quality_profile', 41 | field=models.PositiveIntegerField(default=0), 42 | ), 43 | migrations.AlterField( 44 | model_name='conreqconfig', 45 | name='sonarr_tv_folder', 46 | field=models.PositiveIntegerField(default=0), 47 | ), 48 | migrations.AlterField( 49 | model_name='conreqconfig', 50 | name='sonarr_tv_quality_profile', 51 | field=models.PositiveIntegerField(default=0), 52 | ), 53 | ] 54 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/0019_remove_conreqconfig_conreq_api_key.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.2 on 2021-05-10 02:32 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('server_settings', '0018_auto_20210428_2039'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='conreqconfig', 15 | name='conreq_api_key', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /conreq/core/server_settings/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/server_settings/migrations/__init__.py -------------------------------------------------------------------------------- /conreq/core/server_settings/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "server_settings" 6 | urlpatterns = [ 7 | path("", views.server_settings, name="main"), 8 | path("sonarr_settings", views.sonarr_settings, name="sonarr_settings"), 9 | path("radarr_settings", views.radarr_settings, name="radarr_settings"), 10 | path("update_settings", views.update_settings, name="update_settings"), 11 | ] 12 | -------------------------------------------------------------------------------- /conreq/core/sign_up/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/sign_up/__init__.py -------------------------------------------------------------------------------- /conreq/core/sign_up/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SignUpConfig(AppConfig): 5 | name = "conreq.core.sign_up" 6 | -------------------------------------------------------------------------------- /conreq/core/sign_up/forms.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.contrib.auth.forms import UserCreationForm 3 | 4 | 5 | class UserForm(UserCreationForm): 6 | class Meta: 7 | model = get_user_model() 8 | fields = ("username", "email", "password1", "password2") 9 | -------------------------------------------------------------------------------- /conreq/core/sign_up/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/sign_up/migrations/__init__.py -------------------------------------------------------------------------------- /conreq/core/sign_up/templates/modal/default.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conreq/core/sign_up/templates/registration/sign_in.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load conreq_tags %} 3 | 4 | 5 | 6 | 7 | {% include "primary/head_content_slim.html" %} 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 |
22 | 23 | 37 | 38 | {% if ""|email_enabled %} 39 |
40 | Forgot Password 41 |
42 | {% endif %} 43 | 44 |
45 |
46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /conreq/core/sign_up/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "sign_up" 6 | urlpatterns = [ 7 | path("", views.invite, name="invite_code"), 8 | path( 9 | "generate_invite_code/", views.generate_invite_code, name="generate_invite_code" 10 | ), 11 | ] 12 | -------------------------------------------------------------------------------- /conreq/core/tmdb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/tmdb/__init__.py -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/cards/artwork.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 |
3 | 4 | 7 | 8 |
-------------------------------------------------------------------------------- /conreq/core/tmdb/templates/cards/casted.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load conreq_tags %} 3 |
4 | 5 | 6 | 7 | 8 | {% if card.requested_by and user.is_staff %} 9 |
10 | {{ card.requested_by }} 11 |
12 | {% endif %} 13 | 14 |
15 | {% if card.content_type == "tv" %} 16 | 17 | {% elif card.content_type == "movie" %} 18 | 19 | {% endif %} 20 |
21 | 22 |
23 | {% if card.poster_path %} 24 | {% firstof card.name card.title 'Unknown' %} 27 | {% else %} 28 | {% firstof card.name card.title 'Unknown' %} 30 | {% endif %} 31 |
32 |
33 | 34 | 35 | 38 | 39 | 40 |
41 |
42 |
{% firstof card.name card.title "Unknown" %}
43 | {% if card.availability == "Available" %} 44 |
45 | {% elif card.availability == "Partial" %} 46 |
47 | {% else %} 48 |
49 | {% endif %} 50 |
{% firstof card.character card.job card.department "Unknown Role"%} 51 |
52 |
53 | 54 |
-------------------------------------------------------------------------------- /conreq/core/tmdb/templates/cards/person.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load conreq_tags %} 3 | -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/cards/poster.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load conreq_tags %} 3 |
4 | 5 | 6 | 7 | 8 | {% if card.requested_by and user.is_staff %} 9 |
10 | {{ card.requested_by }} 11 |
12 | {% endif %} 13 | 14 |
15 | {% if card.content_type == "tv" %} 16 | 17 | {% elif card.content_type == "movie" %} 18 | 19 | {% endif %} 20 |
21 | 22 |
23 | {% firstof card.name card.title 'Unknown' %} 26 |
27 |
28 | 29 | 30 | 33 | 34 | 35 |
36 |
37 |
{% firstof card.name card.title "Unknown" %}
38 | {% if card.availability == "Available" %} 39 |
40 | {% elif card.availability == "Partial" %} 41 |
42 | {% else %} 43 |
44 | {% endif %} 45 |
{{card.overview}}
46 |
47 | 48 |
-------------------------------------------------------------------------------- /conreq/core/tmdb/templates/cards/review.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
"
5 | {{review.content}} 6 |
"
7 |
8 |
- {{review.author}}
9 |
10 |
-------------------------------------------------------------------------------- /conreq/core/tmdb/templates/compressed/simple_posters_css.html: -------------------------------------------------------------------------------- 1 | {% load static compress %} 2 | {% load static %} 3 | {% compress css %} 4 | 5 | {% endcompress %} -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/dropdown/item.html: -------------------------------------------------------------------------------- 1 | {{ dropdown_item.label }} -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/modal/discover_filter_advanced.html: -------------------------------------------------------------------------------- 1 | {% include "primary/loading_animation.html" %} 2 | -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/modal/discover_filter_simple.html: -------------------------------------------------------------------------------- 1 | {% include "primary/loading_animation.html" %} 2 | -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/modal/report_issue.html: -------------------------------------------------------------------------------- 1 | {% include "primary/loading_animation.html" %} 2 | -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/viewport/components/more_info_collection.html: -------------------------------------------------------------------------------- 1 | {% if collection.parts %} 2 | 21 | {% endif %} -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/viewport/components/more_info_recommended.html: -------------------------------------------------------------------------------- 1 | {% if recommended.results and results_contain_valid_id %} 2 | 23 | {% endif %} -------------------------------------------------------------------------------- /conreq/core/tmdb/templates/viewport/discover.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% csrf_token %} 3 | {% include "primary/loading_animation_container.html" %} 4 | {% include "primary/searchbar.html" %} 5 | 6 | 7 |
8 | {% if not all_cards %} 9 |
10 | No results were found! 11 |
12 | {% endif %} 13 | 14 | {% for card in all_cards %} 15 | {% if card.conreq_valid_id %} 16 | {% include "cards/poster.html" %} 17 | {% endif %} 18 | {% endfor %} 19 |
-------------------------------------------------------------------------------- /conreq/core/tmdb/templates/viewport/manage_users.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% csrf_token %} 3 | {% include "primary/loading_animation_container.html" %} 4 | 5 | 6 |
7 |
8 |
9 |
10 |

User Management

11 |
14 | 15 | Invite 16 |
17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {% for account in users %} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 45 | 46 | {% endfor %} 47 | 48 | 49 |
UsernameJoinedLast SeenStaff
{{ account.username }}{{ account.date_joined|date:"Y-m-d @ H:i" }}{{ account.last_login|date:"Y-m-d @ H:i" }}{{ account.is_staff|yesno:"Yes,No" }} 39 |
42 | 43 |
44 |
50 | 51 |
52 |
-------------------------------------------------------------------------------- /conreq/core/tmdb/templates/viewport/reported_issues.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% csrf_token %} 3 | {% include "primary/loading_animation_container.html" %} 4 | 5 | 6 |
7 | {% if not all_cards %} 8 |
9 | No issues were found! Try reporting something first! 10 |
11 | {% endif %} 12 | 13 | {% if all_cards %} 14 | {% for card in all_cards %} 15 | {% include "cards/issue.html" %} 16 | {% endfor %} 17 | {% endif %} 18 |
-------------------------------------------------------------------------------- /conreq/core/tmdb/templates/viewport/requests.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% csrf_token %} 3 | {% include "primary/loading_animation_container.html" %} 4 | 5 | 6 |
7 | {% if not all_cards %} 8 |
9 | No requests were found! Try requesting something first! 10 |
11 | {% endif %} 12 | 13 | {% for card in all_cards %} 14 | {% if card %} 15 | {% include "cards/poster.html" %} 16 | {% endif %} 17 | {% endfor %} 18 |
-------------------------------------------------------------------------------- /conreq/core/tmdb/templates/viewport/search.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load solo_tags %} 3 | {% load conreq_tags %} 4 | {% get_solo 'server_settings.ConreqConfig' as server_settings %} 5 | {% csrf_token %} 6 | {% include "primary/loading_animation_container.html" %} 7 | {% include "primary/searchbar.html" %} 8 | 10 | 11 | -------------------------------------------------------------------------------- /conreq/core/user_requests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/user_requests/__init__.py -------------------------------------------------------------------------------- /conreq/core/user_requests/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from conreq.core.user_requests.models import UserRequest 4 | 5 | 6 | # Register your models here. 7 | @admin.register(UserRequest) 8 | class AllUserRequests(admin.ModelAdmin): 9 | pass 10 | -------------------------------------------------------------------------------- /conreq/core/user_requests/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UserRequestsConfig(AppConfig): 5 | name = "conreq.core.user_requests" 6 | -------------------------------------------------------------------------------- /conreq/core/user_requests/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-30 07:39 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='UserRequest', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('content_id', models.CharField(max_length=30)), 22 | ('source', models.CharField(max_length=30)), 23 | ('content_type', models.CharField(max_length=30)), 24 | ('requested_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /conreq/core/user_requests/migrations/0002_userrequest_date_requested.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-31 09:47 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('user_requests', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='userrequest', 16 | name='date_requested', 17 | field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /conreq/core/user_requests/migrations/0003_alter_userrequest_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-04-13 05:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('user_requests', '0002_userrequest_date_requested'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='userrequest', 15 | name='id', 16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /conreq/core/user_requests/migrations/0004_alter_userrequest_requested_by.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.2 on 2021-05-09 02:37 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('user_requests', '0003_alter_userrequest_id'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='userrequest', 18 | name='requested_by', 19 | field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /conreq/core/user_requests/migrations/0005_remove_userrequest_source.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-06 06:44 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('user_requests', '0004_alter_userrequest_requested_by'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='userrequest', 15 | name='source', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /conreq/core/user_requests/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/user_requests/migrations/__init__.py -------------------------------------------------------------------------------- /conreq/core/user_requests/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.db import models 3 | 4 | User = get_user_model() 5 | 6 | # Create your models here. 7 | class UserRequest(models.Model): 8 | content_id = models.CharField(max_length=30) 9 | content_type = models.CharField(max_length=30) 10 | requested_by = models.ForeignKey( 11 | User, on_delete=models.CASCADE, default=None, null=True, blank=True 12 | ) 13 | date_requested = models.DateTimeField(auto_now_add=True) 14 | -------------------------------------------------------------------------------- /conreq/core/user_requests/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "user_requests" 6 | urlpatterns = [ 7 | path("", views.request_content, name="request_content"), 8 | path("my_requests/", views.my_requests, name="my_requests"), 9 | path("all_requests/", views.all_requests, name="all_requests"), 10 | ] 11 | -------------------------------------------------------------------------------- /conreq/core/websockets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/core/websockets/__init__.py -------------------------------------------------------------------------------- /conreq/core/websockets/consumers.py: -------------------------------------------------------------------------------- 1 | """Anything used to construct a websocket endpoint""" 2 | from channels.auth import login 3 | from channels.db import database_sync_to_async as convert_to_async 4 | from channels.generic.websocket import AsyncJsonWebsocketConsumer 5 | from django.contrib.auth.models import AnonymousUser 6 | 7 | from conreq.utils import log 8 | 9 | _logger = log.get_logger(__name__) 10 | 11 | 12 | class CommandConsumer(AsyncJsonWebsocketConsumer): 13 | """Conreq websocket communication.""" 14 | 15 | # INITIAL CONNECTION 16 | async def connect(self): 17 | """When the browser attempts to connect to the server.""" 18 | # Accept the connection 19 | await self.accept() 20 | 21 | # Attempt logging in the user 22 | try: 23 | # Log in the user to this session. 24 | await login(self.scope, self.scope["user"]) 25 | # Save the session to the database 26 | await convert_to_async(self.scope["session"].save)() 27 | except Exception: 28 | # User could not be logged in 29 | log.handler( 30 | "Websocket login failure on initial connection!", 31 | log.ERROR, 32 | _logger, 33 | ) 34 | await self.__forbidden() 35 | 36 | # RECEIVING COMMANDS 37 | async def receive_json(self, content, **kwargs): 38 | """When the browser attempts to send a message to the server.""" 39 | log.handler( 40 | content, 41 | log.INFO, 42 | _logger, 43 | ) 44 | # Reject users that aren't logged in 45 | if ( 46 | isinstance(self.scope["user"], AnonymousUser) 47 | or not self.scope["user"].is_authenticated 48 | ): 49 | await self.__forbidden() 50 | 51 | # COMMAND RESPONSE: FORBIDDEN 52 | async def __forbidden(self): 53 | response = {"command_name": "forbidden"} 54 | await self.send_json(response) 55 | -------------------------------------------------------------------------------- /conreq/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/conreq/utils/__init__.py -------------------------------------------------------------------------------- /conreq/utils/database.py: -------------------------------------------------------------------------------- 1 | def add_unique_to_db(model, **kwargs): 2 | """Adds a row to the database only if all parameters are unique.""" 3 | if not model.objects.filter(**kwargs): 4 | new_request = model(**kwargs) 5 | new_request.clean_fields() 6 | new_request.save() 7 | return new_request 8 | return None 9 | -------------------------------------------------------------------------------- /conreq/utils/debug.py: -------------------------------------------------------------------------------- 1 | """Capabilities used while in DEBUG, that turn off in production environments.""" 2 | from functools import wraps 3 | 4 | from conreq.utils.environment import get_debug 5 | 6 | 7 | # pylint: disable=invalid-name,too-few-public-methods 8 | # Helper function for doing nothing 9 | def do_nothing(function=None): 10 | def decorator(view_func): 11 | @wraps(view_func) 12 | def _wrapped_view(request, *args, **kwargs): 13 | return view_func(request, *args, **kwargs) 14 | 15 | return _wrapped_view 16 | 17 | return decorator(function) 18 | 19 | 20 | # Helper class for doing nothing 21 | class DoNothing: 22 | def __call__(self, target): 23 | return target 24 | 25 | 26 | # Set functionality depending on whether we are in DEBUG=True 27 | if get_debug(): 28 | from silk.profiling.profiler import silk_profile 29 | 30 | class performance_metrics(silk_profile): 31 | pass 32 | 33 | 34 | else: 35 | 36 | class performance_metrics(DoNothing): 37 | pass 38 | -------------------------------------------------------------------------------- /conreq/utils/generic.py: -------------------------------------------------------------------------------- 1 | """Generic functions to be used anywhere. All functions only have stdlib dependencies.""" 2 | import os 3 | import pkgutil 4 | from re import sub 5 | 6 | 7 | def is_key_value_in_list( 8 | key: any, value: any, search_list: list, return_item: bool = False 9 | ) -> bool: 10 | """Iterate through a list of dicts to check if a specific key/value pair exists.""" 11 | if isinstance(search_list, list): 12 | for item in search_list: 13 | if item.__contains__(key) and item[key] == value: 14 | if return_item: 15 | return item 16 | return True 17 | return False 18 | 19 | 20 | def remove_duplicates_from_list(duplicate_list: list) -> list: 21 | """Returns a list that contains no duplicate values""" 22 | return list(dict.fromkeys(duplicate_list)) 23 | 24 | 25 | def clean_string(string: str) -> str: 26 | """Removes non-alphanumerics from a string""" 27 | return sub(r"\W+", "", string).lower() 28 | 29 | 30 | def str_to_bool(string: str, default_value: bool = True) -> bool: 31 | """Converts a string into a boolean.""" 32 | if isinstance(string, str): 33 | if string.lower() == "true" or string == "1": 34 | return True 35 | if string.lower() == "false" or string == "0": 36 | return False 37 | return default_value 38 | 39 | 40 | def str_to_int(value: str, default_value: int = 0) -> int: 41 | """Converts a string into a integer.""" 42 | if isinstance(value, str) and value.isdigit(): 43 | return int(value) 44 | return default_value 45 | 46 | 47 | def list_modules(path: str, prefix: str = "") -> list[str]: 48 | """Returns all modules in a path""" 49 | return [name for _, name, _ in pkgutil.iter_modules([path], prefix=prefix)] 50 | 51 | 52 | def list_modules_with(path: str, submodule_name: str, prefix: str = "") -> list[str]: 53 | """Returns a tuple of all modules containing module name and an importable path to 'example.module.urls'""" 54 | modules = list_modules(path) 55 | module_files = [] 56 | for module in modules: 57 | module_path = os.path.join(path, module) 58 | if submodule_name in list_modules(module_path): 59 | urls_path = prefix + module + "." + submodule_name 60 | module_files.append((module, urls_path)) 61 | return module_files 62 | -------------------------------------------------------------------------------- /conreq/utils/log.py: -------------------------------------------------------------------------------- 1 | """Conreq Logging: Simplified logging module.""" 2 | import logging 3 | from logging import Logger 4 | from traceback import format_exc 5 | 6 | # Globals 7 | DEBUG = logging.DEBUG 8 | INFO = logging.INFO 9 | WARNING = logging.WARNING 10 | ERROR = logging.ERROR 11 | CRITICAL = logging.CRITICAL 12 | 13 | # Function reference to Py Logging getLogger 14 | get_logger = logging.getLogger 15 | 16 | 17 | def handler(msg: str, level: int, logger: Logger) -> None: 18 | """Submits a message to the log handler. 19 | 20 | Args: 21 | message: A string containing a verbose log message. 22 | logger: A logger objected obtained from get_logger(). 23 | level: Logging module log level (ex. logging.WARNING) 24 | """ 25 | # Remove trailing and proceeding whitespace from the message 26 | message = str(msg).rstrip("\n").rstrip().lstrip("\n").lstrip() 27 | 28 | # Log within a different stream depending on severity 29 | if level == DEBUG: 30 | logger.debug(message) 31 | elif level == INFO: 32 | logger.info(message) 33 | elif level == WARNING: 34 | logger.warning(message) 35 | elif level == ERROR: 36 | logger.error(message + "\n" + format_exc()) 37 | elif level == CRITICAL: 38 | logger.critical(message + "\n" + format_exc()) 39 | -------------------------------------------------------------------------------- /conreq/utils/user_apps.py: -------------------------------------------------------------------------------- 1 | import os 2 | from importlib import import_module 3 | 4 | from django.conf import settings 5 | 6 | from .generic import list_modules 7 | 8 | APPS_DIR = getattr(settings, "APPS_DIR") 9 | 10 | 11 | def list_user_apps() -> list[str]: 12 | apps_list = [] 13 | user_apps = list_modules(APPS_DIR) 14 | for user_app in user_apps: 15 | package_dict = {} 16 | package_dict["name"] = user_app 17 | package_dict["modules"] = {} 18 | app_dir = os.path.join(APPS_DIR, user_app) 19 | for package in list_modules(app_dir): 20 | sub_app_dir = os.path.join(app_dir, package) 21 | if package == "settings": 22 | module = import_module(user_app + "." + package) 23 | package_dict["settings"] = [ 24 | item for item in dir(module) if not item.startswith("_") 25 | ] 26 | else: 27 | package_dict["modules"][package] = [] 28 | for module in list_modules(sub_app_dir): 29 | package_dict["modules"][package].append( 30 | (module, user_app + "." + package + "." + module) 31 | ) 32 | apps_list.append(package_dict) 33 | 34 | return apps_list 35 | 36 | 37 | def list_user_apps_with(submodule_name: str) -> list[str]: 38 | apps_list = [] 39 | user_apps = list_modules(APPS_DIR) 40 | for user_app in user_apps: 41 | app_dir = os.path.join(APPS_DIR, user_app) 42 | for package in list_modules(app_dir): 43 | sub_app_dir = os.path.join(app_dir, package) 44 | modules = list_modules(sub_app_dir) 45 | if submodule_name in modules: 46 | apps_list.append(user_app + "." + package + "." + submodule_name) 47 | return apps_list 48 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | nav: 2 | - Home: index.md 3 | - Install: 4 | - Docker: install/docker.md 5 | - Windows NSSM: install/nssm.md 6 | - Configuration: 7 | - Environment Variables: configure/env_vars.md 8 | - Built-in Webserver: configure/webserver.md 9 | - Custom CSS/JS: configure/static_files.md 10 | - Organizr Authentication: configure/organizr.md 11 | - MySQL Database: configure/mysql.md 12 | - Development: 13 | - Running Conreq Manually: develop/run_conreq.md 14 | - Building the Docker: develop/build_docker.md 15 | - Creating Custom Apps: develop/creating_apps.md 16 | - Contribute: 17 | - Updating the Docs: contrib/docs.md 18 | - Create an App Store listing: contrib/apps.md 19 | 20 | theme: 21 | name: material 22 | palette: 23 | primary: teal 24 | accent: teal 25 | features: 26 | - navigation.instant 27 | - navigation.tabs 28 | - navigation.tabs.sticky 29 | - navigation.top 30 | - content.code.copy 31 | - search.highlight 32 | - navigation.sections 33 | - toc.integrate 34 | 35 | icon: 36 | repo: fontawesome/brands/github 37 | 38 | markdown_extensions: 39 | - toc: 40 | permalink: true 41 | - pymdownx.emoji: 42 | emoji_index: !!python/name:material.extensions.emoji.twemoji 43 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 44 | - pymdownx.tabbed: 45 | alternate_style: true 46 | - pymdownx.highlight: 47 | linenums: true 48 | - pymdownx.superfences 49 | - pymdownx.details 50 | - pymdownx.inlinehilite 51 | - admonition 52 | - attr_list 53 | - md_in_html 54 | - pymdownx.keys 55 | 56 | plugins: 57 | - search 58 | - include-markdown 59 | - git-authors 60 | - minify: 61 | minify_html: true 62 | minify_js: true 63 | minify_css: true 64 | cache_safe: true 65 | - git-revision-date-localized: 66 | fallback_to_build_date: true 67 | - spellcheck: 68 | known_words: dictionary.txt 69 | allow_unicode: no 70 | ignore_code: yes 71 | 72 | site_name: Conreq Documentation 73 | site_author: Archmonger 74 | site_description: Conreq Web Platform 75 | repo_url: https://github.com/Archmonger/Conreq/ 76 | site_url: https://archmonger.github.io/Conreq/ 77 | repo_name: Archmonger/Conreq 78 | edit_uri: edit/main/docs/src/ 79 | docs_dir: src 80 | -------------------------------------------------------------------------------- /docs/src/configure/mysql.md: -------------------------------------------------------------------------------- 1 | 1. Install and run MySQL using your preferred method. 2 | 3 | 2. Create a file within your Conreq data directory create a MySQL configuration file (`mysql.cnf`). At minimum you must include the following... 4 | 5 | ```toml 6 | [client] 7 | database = conreq 8 | user = db_username 9 | password = db_password 10 | host = 192.168.86.200 11 | default-character-set = utf8 12 | ``` 13 | 14 | _Take a look at MySQL's "Options Files" docs for all available parameters._ 15 | 16 | 3. Set your `DB_ENGINE ` variable to `MYSQL` and your `MYSQL_CONFIG_FILE` variable to the path to your `mysql.cnf` file. 17 | 18 | _If using Unraid/Docker, add this as a Docker variable. Otherwise, search "How to set environment variables in ... Windows 10"_ 19 | -------------------------------------------------------------------------------- /docs/src/configure/organizr.md: -------------------------------------------------------------------------------- 1 | 1. Go to `Server Settings` within Conreq and enable `Organizr User Authentication` 2 | 3 | 2. Set up a `nginx` reverse proxy. 4 | 5 | _If you need help with this, join the [`organizr` Discord](https://discord.com/invite/TrNtY7N) and post in #groups._ 6 | 7 | 3. In your Conreq block within `nginx`, add the following... 8 | 9 | ```nginx 10 | # Sets Conreq to be accessible by all Organizr users. Google "Organizr ServerAuth" for more details. 11 | auth_request /auth-4; 12 | 13 | # Allows Conreq to log in as an Organizr user 14 | auth_request_set $auth_user $upstream_http_x_organizr_user; 15 | proxy_set_header X-WEBAUTH-USER $auth_user; 16 | 17 | # Allows Conreq to know the email address of an Organizr user (optional) 18 | auth_request_set $auth_email $upstream_http_x_organizr_email; 19 | proxy_set_header X-WEBAUTH-EMAIL $auth_email; 20 | 21 | # Allows Conreq to automatically configure Organizr Admins and Co-Admins as Conreq staff members (optional) 22 | auth_request_set $auth_group $upstream_http_x_organizr_group; 23 | proxy_set_header X-WEBAUTH-GROUP $auth_group; 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/src/configure/static_files.md: -------------------------------------------------------------------------------- 1 | Conreq can store and serve any custom files located within your data directory's "static" folder. 2 | 3 | In order to use this to serve custom CSS or JavaScript... 4 | 5 | 1. Put a CSS/JS file into `DATA_DIR/static/` 6 | 2. Restart Conreq 7 | 3. Set your Conreq "Custom CSS/JS" to link to the new content, such as `/static/my_style.css` 8 | 4. Done! 9 | -------------------------------------------------------------------------------- /docs/src/configure/webserver.md: -------------------------------------------------------------------------------- 1 | We use [`hypercorn`](https://github.com/pgjones/hypercorn/) as Conreq's production-grade webserver. This webserver can be directly exposed to the internet. For more information beyond what is in this guide, check out the [`hypercorn` documentation](https://hypercorn.readthedocs.io/en/latest/). 2 | 3 | --- 4 | 5 | The Conreq webserver can be modified through a `hypercorn.toml` file. 6 | 7 | 1. Create a `hypercorn.toml` file within your Conreq data directory (such as `./conreq/data/hypercorn.toml`) 8 | 9 | 2. Populate this `toml` file with any property in the [`hypercorn`'s documentation](https://hypercorn.readthedocs.io/en/latest/how_to_guides/configuring.html#configuration-options). For example... 10 | 11 | ```toml 12 | bind = "0.0.0.0:5357" 13 | h11_max_incomplete_size = 4 14 | keep_alive_timeout = 20 15 | use_reloader = true 16 | workers = 20 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/src/contrib/apps.md: -------------------------------------------------------------------------------- 1 | Work in Progress, Conreq app framework has not been finalized. 2 | -------------------------------------------------------------------------------- /docs/src/contrib/docs.md: -------------------------------------------------------------------------------- 1 | We use [MkDocs](https://www.mkdocs.org/#overview) to create our documentation. For more information beyond what is in this guide, check out the [MkDocs Documentation](https://www.mkdocs.org/#getting-started). 2 | 3 | --- 4 | 5 | ## Setting Up the Environment 6 | 7 | 1. Install `Python 3.x` 8 | 2. Open a terminal at the root of the repository. 9 | 3. Type `pip install -r requirements.txt` to install MkDocs. 10 | 11 | --- 12 | 13 | ## Starting the Preview Webserver 14 | 15 | MkDocs contains a tool to allow you to preview your documentation changes live! In order to use it... 16 | 17 | 1. Open a terminal at the root of the repository. 18 | 2. Type `cd docs` to enter the documentation's source code directory. 19 | 3. Type `mkdocs serve` to start the preview webserver. 20 | 4. Navigate to [`http://127.0.0.1:8000`](http://127.0.0.1:8000) to see the documentation and changes live! 21 | 22 | --- 23 | 24 | ## Adding/Editing a Docs Page 25 | 26 | 1. Create a new markdown file within `source/docs/`, or edit an existing markdown file within this folder. 27 | 2. Fill in this file with any markdown text you want! 28 | 3. _If you made a new file:_ Add this file to the navigation bar within `mkdocs.yml`. 29 | 4. Commit your changes to your GitHub branch. 30 | 5. Submit a GitHub pull request to `Archmonger/Conreq:docs`. 31 | -------------------------------------------------------------------------------- /docs/src/develop/build_docker.md: -------------------------------------------------------------------------------- 1 | ## Software Required 2 | 3 | - Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) 4 | 5 | ## Building the Docker Image 6 | 7 | 1. Pull the repository from GitHub. 8 | 2. Open a terminal at the root of the repository. 9 | 3. Type `docker build . --no-cache -t conreq` to build the Docker image. 10 | -------------------------------------------------------------------------------- /docs/src/develop/creating_apps.md: -------------------------------------------------------------------------------- 1 | Work in Progress, Conreq app framework has not been finalized. 2 | -------------------------------------------------------------------------------- /docs/src/develop/run_conreq.md: -------------------------------------------------------------------------------- 1 | There are two environments Conreq can exist in: [Development](#creating-a-development-environment) and [Production](#creating-a-production-environment). 2 | 3 | If you intend to edit Conreq's code, then proceed with the Development instructions. 4 | 5 | --- 6 | 7 | ## Software Required 8 | 9 | - Install [Python 3.11+](https://www.python.org/downloads/) 10 | - Make sure to select "Add Python 3.x to PATH" during installation. 11 | - Easiest if this is the only version of python on your computer 12 | - _If using Windows_: Install [Visual Studio C++](https://visualstudio.microsoft.com/visual-cpp-build-tools/) (Within this installer, navigate to _C++ Build Tools_. Select _MSVC_ and _Windows 10 SDK_) 13 | - _Optional_: Install [Visual Studio Code](https://code.visualstudio.com/) (Any editor would work) 14 | 15 | ## Creating a Development Environment 16 | 17 | 1. Pull the repository from GitHub. 18 | 2. Open a terminal as administrator at the root of the repository. 19 | 3. _If using Windows_ 20 | - Type `set-executionpolicy remotesigned` and select Yes to All to allow external Python scripts to run on your computer. 21 | 4. Type `python -m venv .venv` to create a Python virtual environment called `.venv`. 22 | 5. Type `./.venv/Scripts/activate` to enter the virtual environment. 23 | 6. Type `pip install -r requirements.txt` to install all Python dependencies within the virtual environment. 24 | 7. Type `python manage.py run_conreq` to run the webserver. 25 | 26 | --- 27 | 28 | ## Creating a Production Environment 29 | 30 | Follow all instructions laid out within [Creating a Development Environment](#creating-a-development-environment), but before running `python manage.py run_conreq` do the following: 31 | 32 | 1. Set your environment variable of `DEBUG` to `false`. The method of doing this will [vary based on operating system](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html). 33 | - Doing this enables _a lot_ of **security features**. 34 | 2. The webserver is configured with good defaults for most cases. But if you want to configure the webserver see our [webserver documentation](../configure/webserver.md) for more information. 35 | 3. Type `python manage.py run_conreq` to run the webserver. 36 | -------------------------------------------------------------------------------- /docs/src/dictionary.txt: -------------------------------------------------------------------------------- 1 | conreq 2 | webserver 3 | unraid 4 | reddit 5 | django 6 | -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | [![Docker Hub](https://img.shields.io/badge/Docker-DockerHub-blue?style=flat-square)](https://hub.docker.com/r/archmonger/conreq) [![GitHub Container Registry](https://img.shields.io/badge/Docker-GitHub-blue?style=flat-square)](https://github.com/Archmonger/Conreq/pkgs/container/conreq) [![Discord](https://img.shields.io/discord/440067432552595457?style=flat-square&label=Discord&logo=discord)](https://discord.gg/gQhGZzEjmX "Chat with the community and get realtime support!") 2 | 3 | ## Welcome to the Conreq Documentation 4 | 5 | Conreq is an extensible self-hosted web platform built around [Django](https://www.djangoproject.com/). Read through the docs to learn how to set up Conreq, or how to contribute to the Conreq repository! 6 | 7 | Want to join the community or have a question? Chat with us on [Discord](https://discord.gg/gQhGZzEjmX) or create a post on [GitHub Discussions](https://github.com/Archmonger/Conreq/discussions)! 8 | 9 | --- 10 | 11 | ## Communities 12 | 13 | [:fontawesome-brands-discord: Discord](https://discord.gg/gQhGZzEjmX){ .md-button .md-button--primary } [:fontawesome-brands-github: GitHub Discussions](https://github.com/Archmonger/Conreq/discussions){ .md-button .md-button--primary } 14 | -------------------------------------------------------------------------------- /docs/src/install/docker.md: -------------------------------------------------------------------------------- 1 | These instructions configure Conreq to run within a Docker. 2 | 3 | Our dockers are published to both [DockerHub](https://hub.docker.com/r/archmonger/conreq/tags) and [GitHub Container Registry](https://github.com/Archmonger/Conreq/pkgs/container/conreq). 4 | 5 | --- 6 | 7 | ## Docker Compose 8 | 9 | 1. Create a `docker-compose.yml` file is located where you want Conreq to run. 10 | 11 | === "`docker-compose.yml`" 12 | 13 | ```yaml 14 | services: 15 | conreq: 16 | image: ghcr.io/archmonger/conreq:latest # Other tags include `develop` and version numbers (e.g. `0.21.1`) 17 | container_name: conreq 18 | environment: 19 | - PUID=99 # Optional 20 | - PGID=100 # Optional 21 | - TZ=America/Los_Angeles # If not set, defaults to UTC 22 | # Any other Conreq environment variables can also be set here 23 | volumes: 24 | - ./config:/config # You can replace ./config with the location you want to store Conreq's data. 25 | ports: 26 | - 7575:7575 27 | ``` 28 | 29 | 2. Run `docker-compose up -d` to start Conreq. The `-d` flag runs the container in the background. 30 | 31 | ## Docker Run 32 | 33 | 1. Run the following command to start Conreq. 34 | 35 | ```bash 36 | docker run -d --name=conreq -e PUID=99 -e PGID=100 -e TZ=America/Los_Angeles -v ./config:/config -p 7575:7575 ghcr.io/archmonger/conreq:latest 37 | ``` 38 | 39 | _Note: You can replace `./config` with the location you want to store Conreq's data._ 40 | 41 | _Note 2: The `-d` flag runs the container in the background._ 42 | -------------------------------------------------------------------------------- /docs/src/install/nssm.md: -------------------------------------------------------------------------------- 1 | These instructions configure Non-Sucking Service Manager (NSSM) to automatically start Conreq as a Windows service. 2 | 3 | A traditional Windows installer is not yet available. 4 | 5 | --- 6 | 7 | ## Software Required 8 | 9 | - Install [Python 3.11+](https://www.python.org/downloads/) 10 | - Make sure to select "Add Python 3.x to PATH" during installation. 11 | - Easiest if this is the only version of python on your computer 12 | - Steps below will not work with NSSM if using the "Windows App Store" version of Python. 13 | - Install [Visual Studio C++](https://visualstudio.microsoft.com/visual-cpp-build-tools/) (Within this installer, navigate to _C++ Build Tools_. Select _MSVC_ and _Windows 10 SDK_) 14 | - Download [NSSM](https://nssm.cc/download) 15 | 16 | ## Installation 17 | 18 | 1. Create the folder `C:\Program Files\Conreq` 19 | 2. Use this folder to follow steps [setting up a Conreq environment](../develop/run_conreq.md#creating-a-production-environment), excluding steps involving `python manage.py run_conreq`. 20 | 3. Unzip NSSM in a separate directory where you can permanently keep it (such as `C:\Program Files\NSSM`). 21 | 4. Open a terminal within your NSSM directory **as administrator**. 22 | 5. Open NSSM by typing `.\win64\nssm.exe install Conreq` 23 | 6. Under the Application tab, browse to the path of your **Conreq `venv`**. 24 | - For example: `C:\Program Files\Conreq\.venv\Scripts\python.exe` 25 | 7. Under the Application tab, browse to the path of your **Conreq repository**. 26 | - For example: `C:\Program Files\Conreq\` 27 | 8. Under the Application tab, set "Arguments" to `manage.py run_conreq` 28 | 9. Click "Install Service". 29 | 10. In your terminal, type `.\win64\nssm.exe start Conreq`. 30 | - You can check to make sure its running as a service by running `nssm status Conreq` 31 | 11. Open any internet browser and navigate to [`http://127.0.0.1:7575/`](http://127.0.0.1:7575/) 32 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "conreq.settings") 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /misc/branding/circle.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/circle.ai -------------------------------------------------------------------------------- /misc/branding/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/circle.png -------------------------------------------------------------------------------- /misc/branding/circle_outline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/circle_outline.ai -------------------------------------------------------------------------------- /misc/branding/circle_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/circle_outline.png -------------------------------------------------------------------------------- /misc/branding/conreq_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/conreq_logo.png -------------------------------------------------------------------------------- /misc/branding/conreq_logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/conreq_logo.psd -------------------------------------------------------------------------------- /misc/branding/conreq_logo_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/conreq_logo_dark.png -------------------------------------------------------------------------------- /misc/branding/conreq_logo_dark.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/conreq_logo_dark.psd -------------------------------------------------------------------------------- /misc/branding/conreq_square.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/conreq_square.ai -------------------------------------------------------------------------------- /misc/branding/conreq_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/conreq_square.png -------------------------------------------------------------------------------- /misc/branding/no_bg_dark.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/no_bg_dark.ai -------------------------------------------------------------------------------- /misc/branding/no_bg_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/no_bg_dark.png -------------------------------------------------------------------------------- /misc/branding/no_bg_gradient.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/no_bg_gradient.ai -------------------------------------------------------------------------------- /misc/branding/no_bg_gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/no_bg_gradient.png -------------------------------------------------------------------------------- /misc/branding/no_bg_light.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/no_bg_light.ai -------------------------------------------------------------------------------- /misc/branding/no_bg_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/no_bg_light.png -------------------------------------------------------------------------------- /misc/branding/rounded_square.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/rounded_square.ai -------------------------------------------------------------------------------- /misc/branding/rounded_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/rounded_square.png -------------------------------------------------------------------------------- /misc/branding/rounded_square_outline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/rounded_square_outline.ai -------------------------------------------------------------------------------- /misc/branding/rounded_square_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/branding/rounded_square_outline.png -------------------------------------------------------------------------------- /misc/legal/fontawesome_attribution.js: -------------------------------------------------------------------------------- 1 | console.log(`Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com 2 | License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 3 | `) -------------------------------------------------------------------------------- /misc/legal/fontawesome_license.txt: -------------------------------------------------------------------------------- 1 | Font Awesome Free License 2 | ------------------------- 3 | 4 | Font Awesome Free is free, open source, and GPL friendly. You can use it for 5 | commercial projects, open source projects, or really almost whatever you want. 6 | Full Font Awesome Free license: https://fontawesome.com/license/free. 7 | 8 | # Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) 9 | In the Font Awesome Free download, the CC BY 4.0 license applies to all icons 10 | packaged as SVG and JS file types. 11 | 12 | # Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL) 13 | In the Font Awesome Free download, the SIL OFL license applies to all icons 14 | packaged as web and desktop font files. 15 | 16 | # Code: MIT License (https://opensource.org/licenses/MIT) 17 | In the Font Awesome Free download, the MIT license applies to all non-font and 18 | non-icon files. 19 | 20 | # Attribution 21 | Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font 22 | Awesome Free files already contain embedded comments with sufficient 23 | attribution, so you shouldn't need to do anything additional when using these 24 | files normally. 25 | 26 | We've kept attribution comments terse, so we ask that you do not actively work 27 | to remove them from files, especially code. They're a great way for folks to 28 | learn about Font Awesome. 29 | 30 | # Brand Icons 31 | All brand icons are trademarks of their respective owners. The use of these 32 | trademarks does not indicate endorsement of the trademark holder by Font 33 | Awesome, nor vice versa. **Please do not use brand logos for any purpose except 34 | to represent the company, product, or service to which they refer.** 35 | -------------------------------------------------------------------------------- /misc/other/person_placeholder.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/other/person_placeholder.psd -------------------------------------------------------------------------------- /misc/other/poster_placeholder.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/other/poster_placeholder.psd -------------------------------------------------------------------------------- /misc/screenshots/desktop_discover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/desktop_discover.png -------------------------------------------------------------------------------- /misc/screenshots/desktop_modal_episode_selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/desktop_modal_episode_selection.png -------------------------------------------------------------------------------- /misc/screenshots/desktop_modal_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/desktop_modal_filter.png -------------------------------------------------------------------------------- /misc/screenshots/desktop_modal_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/desktop_modal_preview.png -------------------------------------------------------------------------------- /misc/screenshots/desktop_more_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/desktop_more_info.png -------------------------------------------------------------------------------- /misc/screenshots/desktop_registration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/desktop_registration.png -------------------------------------------------------------------------------- /misc/screenshots/desktop_sign_in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/desktop_sign_in.png -------------------------------------------------------------------------------- /misc/screenshots/mobile_discover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/mobile_discover.png -------------------------------------------------------------------------------- /misc/screenshots/mobile_modal_episode_selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/mobile_modal_episode_selection.png -------------------------------------------------------------------------------- /misc/screenshots/mobile_modal_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/mobile_modal_filter.png -------------------------------------------------------------------------------- /misc/screenshots/mobile_more_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/mobile_more_info.png -------------------------------------------------------------------------------- /misc/screenshots/mobile_registration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/mobile_registration.png -------------------------------------------------------------------------------- /misc/screenshots/mobile_sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/mobile_sidebar.png -------------------------------------------------------------------------------- /misc/screenshots/mobile_sign_in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archmonger/Conreq/4802485fb3fe3aa47368dcccf2022c85c3cbe3ce/misc/screenshots/mobile_sign_in.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -r requirements/main.txt 2 | -r requirements/dev.txt 3 | -r requirements/docs.txt 4 | -------------------------------------------------------------------------------- /requirements/dev.txt: -------------------------------------------------------------------------------- 1 | black 2 | pylint 3 | rope 4 | pylint_django -------------------------------------------------------------------------------- /requirements/docs.txt: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-material 3 | mkdocs-git-revision-date-localized-plugin 4 | mkdocs-include-markdown-plugin 5 | linkcheckmd 6 | mkdocs-spellcheck[all] 7 | mkdocs-git-authors-plugin 8 | mkdocs-minify-plugin 9 | mike 10 | -------------------------------------------------------------------------------- /requirements/main.txt: -------------------------------------------------------------------------------- 1 | channels[daphne]==4.0.0 2 | diskcache==5.6.3 3 | django==3.2.25 4 | django-cleanup==8.1.0 5 | django-compression-middleware==0.5.0 6 | django-compressor==4.4 7 | django-htmlmin==0.11.0 8 | django-model-utils==4.5.1 9 | django-searchable-encrypted-fields==0.2.1 10 | django-silk==5.1.0 11 | django-solo==2.2.0 12 | django-url-or-relative-url-field==0.2.0 13 | djangorestframework==3.15.1 14 | djangorestframework-api-key==3.0.0 15 | docutils==0.21.2 16 | drf-yasg==1.21.7 17 | huey==2.5.1 18 | hypercorn[h3]==0.17.3 19 | jsonfield==3.1.0 20 | markdown==3.6.0 21 | pwned-passwords-django==2.1 22 | pyarr==5.2.0 23 | pymysql==1.1.1 24 | python-dotenv==1.0.1 25 | strsim==0.0.3 26 | titlecase==2.4.1 27 | tmdbsimple==2.9.1 28 | Twisted[tls,http2]==24.3.0 29 | tzlocal==5.2 30 | whitenoise[brotli]==6.7.0 31 | attrs 32 | --------------------------------------------------------------------------------