Spaces:
				
			
			
	
			
			
		Runtime error
		
	
	
	
			
			
	
	
	
	
		
		
		Runtime error
		
	Upload folder using huggingface_hub (#1)
Browse files- Upload folder using huggingface_hub (1dc2ed635218271810de58cdcff8d71e71a032e5)
- .github/ISSUE_TEMPLATE/bug-form.yml +58 -0
 - .github/ISSUE_TEMPLATE/config.yml +11 -0
 - .github/ISSUE_TEMPLATE/feature_request.yml +45 -0
 - .github/workflows/build.yml +47 -0
 - .github/workflows/del-old.yml +15 -0
 - .gitignore +14 -0
 - CHANGELOG.md +97 -0
 - CODE_OF_CONDUCT.md +128 -0
 - CONTRIBUTING.md +12 -0
 - LICENSE +21 -0
 - base.py +1065 -0
 - cli.py +115 -0
 - colors.py +28 -0
 - default-duce-cli-settings.json +64 -0
 - default-duce-gui-settings.json +60 -0
 - extra/DUCE-LOGO.ico +0 -0
 - extra/DUCE-LOGO.png +0 -0
 - extra/duce-gui-main.png +0 -0
 - extra/promo.gif +0 -0
 - gui.py +680 -0
 - images.py +9 -0
 - requirements.txt +9 -0
 
    	
        .github/ISSUE_TEMPLATE/bug-form.yml
    ADDED
    
    | 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            name: Bug Report
         
     | 
| 2 | 
         
            +
            description: Please let us know if you're facing any problems, errors, or unexpected behavior.
         
     | 
| 3 | 
         
            +
            title: "[Bug]: "
         
     | 
| 4 | 
         
            +
            labels: ["Type: Bug", "Status: TBC"]
         
     | 
| 5 | 
         
            +
            body:
         
     | 
| 6 | 
         
            +
              - type: markdown  
         
     | 
| 7 | 
         
            +
                attributes:
         
     | 
| 8 | 
         
            +
                  value: |
         
     | 
| 9 | 
         
            +
                    Thank you for taking the time to fill out this bug report!
         
     | 
| 10 | 
         
            +
             
     | 
| 11 | 
         
            +
              - type: textarea
         
     | 
| 12 | 
         
            +
                id: what-happened
         
     | 
| 13 | 
         
            +
                attributes:
         
     | 
| 14 | 
         
            +
                  label: What happened?
         
     | 
| 15 | 
         
            +
                  description: |
         
     | 
| 16 | 
         
            +
                    Please describe the issue you encountered and what you expected to happen.
         
     | 
| 17 | 
         
            +
                    You can also attach images or log files by highlighting this area and dragging the files in.
         
     | 
| 18 | 
         
            +
                  placeholder: Describe your experience.
         
     | 
| 19 | 
         
            +
                validations:
         
     | 
| 20 | 
         
            +
                  required: true
         
     | 
| 21 | 
         
            +
             
     | 
| 22 | 
         
            +
              - type: dropdown
         
     | 
| 23 | 
         
            +
                id: enroller
         
     | 
| 24 | 
         
            +
                attributes:
         
     | 
| 25 | 
         
            +
                  label: Enroller
         
     | 
| 26 | 
         
            +
                  description: Which Enroller caused this issue?
         
     | 
| 27 | 
         
            +
                  options:
         
     | 
| 28 | 
         
            +
                    - GUI
         
     | 
| 29 | 
         
            +
                    - CLI
         
     | 
| 30 | 
         
            +
                validations:
         
     | 
| 31 | 
         
            +
                  required: true
         
     | 
| 32 | 
         
            +
             
     | 
| 33 | 
         
            +
              - type: dropdown
         
     | 
| 34 | 
         
            +
                id: os
         
     | 
| 35 | 
         
            +
                attributes:
         
     | 
| 36 | 
         
            +
                  label: OS
         
     | 
| 37 | 
         
            +
                  options:
         
     | 
| 38 | 
         
            +
                    - Windows
         
     | 
| 39 | 
         
            +
                    - Linux-Distro
         
     | 
| 40 | 
         
            +
                    - Mac-OS
         
     | 
| 41 | 
         
            +
                validations:
         
     | 
| 42 | 
         
            +
                  required: true
         
     | 
| 43 | 
         
            +
             
     | 
| 44 | 
         
            +
              - type: textarea
         
     | 
| 45 | 
         
            +
                id: logs
         
     | 
| 46 | 
         
            +
                attributes:
         
     | 
| 47 | 
         
            +
                  label: Relevant log output
         
     | 
| 48 | 
         
            +
                  description: Please copy and paste any relevant log output. It will be automatically formatted as code.
         
     | 
| 49 | 
         
            +
                  render: shell
         
     | 
| 50 | 
         
            +
             
     | 
| 51 | 
         
            +
              - type: checkboxes
         
     | 
| 52 | 
         
            +
                id: terms
         
     | 
| 53 | 
         
            +
                attributes:
         
     | 
| 54 | 
         
            +
                  label: Terms
         
     | 
| 55 | 
         
            +
                  description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/techtanic/Discounted-Udemy-Course-Enroller/blob/master/CODE_OF_CONDUCT.md)
         
     | 
| 56 | 
         
            +
                  options:
         
     | 
| 57 | 
         
            +
                    - label: I am using the latest available version.
         
     | 
| 58 | 
         
            +
                      required: true
         
     | 
    	
        .github/ISSUE_TEMPLATE/config.yml
    ADDED
    
    | 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            blank_issues_enabled: false
         
     | 
| 2 | 
         
            +
            contact_links:
         
     | 
| 3 | 
         
            +
            - name: 💬 Discord
         
     | 
| 4 | 
         
            +
              url: https://discord.gg/wFsfhJh4Rh
         
     | 
| 5 | 
         
            +
              about: |
         
     | 
| 6 | 
         
            +
                For any other help or just discussion. 
         
     | 
| 7 | 
         
            +
                
         
     | 
| 8 | 
         
            +
            - name: Telegram
         
     | 
| 9 | 
         
            +
              url: https://t.me/techtanic
         
     | 
| 10 | 
         
            +
              about: |
         
     | 
| 11 | 
         
            +
                For any other help or just discussion. 
         
     | 
    	
        .github/ISSUE_TEMPLATE/feature_request.yml
    ADDED
    
    | 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            name: Feature Request
         
     | 
| 2 | 
         
            +
            description: Suggest an idea for this project
         
     | 
| 3 | 
         
            +
            title: "[Feature]: "
         
     | 
| 4 | 
         
            +
            labels: ["Type: Enhancement"]
         
     | 
| 5 | 
         
            +
            body:
         
     | 
| 6 | 
         
            +
              - type: markdown
         
     | 
| 7 | 
         
            +
                attributes:
         
     | 
| 8 | 
         
            +
                  value: |
         
     | 
| 9 | 
         
            +
                    Thanks for taking the time to suggest a feature for this project!
         
     | 
| 10 | 
         
            +
             
     | 
| 11 | 
         
            +
              - type: textarea
         
     | 
| 12 | 
         
            +
                id: problem
         
     | 
| 13 | 
         
            +
                attributes:
         
     | 
| 14 | 
         
            +
                  label: Is your feature request related to a problem? Please describe.
         
     | 
| 15 | 
         
            +
                  description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
         
     | 
| 16 | 
         
            +
                  placeholder: Describe the problem.
         
     | 
| 17 | 
         
            +
                validations:
         
     | 
| 18 | 
         
            +
                  required: false
         
     | 
| 19 | 
         
            +
             
     | 
| 20 | 
         
            +
              - type: textarea
         
     | 
| 21 | 
         
            +
                id: solution
         
     | 
| 22 | 
         
            +
                attributes:
         
     | 
| 23 | 
         
            +
                  label: Describe the solution you'd like
         
     | 
| 24 | 
         
            +
                  description: A clear and concise description of what you want to happen.
         
     | 
| 25 | 
         
            +
                  placeholder: Describe the solution.
         
     | 
| 26 | 
         
            +
                validations:
         
     | 
| 27 | 
         
            +
                  required: true
         
     | 
| 28 | 
         
            +
             
     | 
| 29 | 
         
            +
              - type: textarea
         
     | 
| 30 | 
         
            +
                id: alternatives
         
     | 
| 31 | 
         
            +
                attributes:
         
     | 
| 32 | 
         
            +
                  label: Describe alternatives you've considered
         
     | 
| 33 | 
         
            +
                  description: A clear and concise description of any alternative solutions or features you've considered.
         
     | 
| 34 | 
         
            +
                  placeholder: Describe the alternatives.
         
     | 
| 35 | 
         
            +
                validations:
         
     | 
| 36 | 
         
            +
                  required: false
         
     | 
| 37 | 
         
            +
             
     | 
| 38 | 
         
            +
              - type: textarea
         
     | 
| 39 | 
         
            +
                id: additional-context
         
     | 
| 40 | 
         
            +
                attributes:
         
     | 
| 41 | 
         
            +
                  label: Additional context
         
     | 
| 42 | 
         
            +
                  description: Add any other context or screenshots about the feature request here.
         
     | 
| 43 | 
         
            +
                  placeholder: Add any other context.
         
     | 
| 44 | 
         
            +
                validations:
         
     | 
| 45 | 
         
            +
                  required: false
         
     | 
    	
        .github/workflows/build.yml
    ADDED
    
    | 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            name: Build
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            on:
         
     | 
| 4 | 
         
            +
              workflow_dispatch:
         
     | 
| 5 | 
         
            +
              push:
         
     | 
| 6 | 
         
            +
                branches:
         
     | 
| 7 | 
         
            +
                  - master
         
     | 
| 8 | 
         
            +
             
     | 
| 9 | 
         
            +
            jobs:
         
     | 
| 10 | 
         
            +
              build:
         
     | 
| 11 | 
         
            +
                runs-on: windows-latest
         
     | 
| 12 | 
         
            +
                strategy:
         
     | 
| 13 | 
         
            +
                  matrix:
         
     | 
| 14 | 
         
            +
                    include:
         
     | 
| 15 | 
         
            +
                      - name: "DUCE-GUI-windows"
         
     | 
| 16 | 
         
            +
                        mode: "-w"
         
     | 
| 17 | 
         
            +
                        script: "gui"
         
     | 
| 18 | 
         
            +
                      - name: "DUCE-CLI-windows"
         
     | 
| 19 | 
         
            +
                        mode: "-c"
         
     | 
| 20 | 
         
            +
                        script: "cli"
         
     | 
| 21 | 
         
            +
                steps:
         
     | 
| 22 | 
         
            +
                  - uses: actions/checkout@v4
         
     | 
| 23 | 
         
            +
             
     | 
| 24 | 
         
            +
                  - name: Set up Python
         
     | 
| 25 | 
         
            +
                    uses: actions/setup-python@v5
         
     | 
| 26 | 
         
            +
                    with:
         
     | 
| 27 | 
         
            +
                      python-version: "3.11"
         
     | 
| 28 | 
         
            +
                      cache: "pip"
         
     | 
| 29 | 
         
            +
             
     | 
| 30 | 
         
            +
                  - name: Install dependencies and PyInstaller
         
     | 
| 31 | 
         
            +
                    run: pip install -r requirements.txt pyinstaller -U
         
     | 
| 32 | 
         
            +
              
         
     | 
| 33 | 
         
            +
                  - name: Build ${{ matrix.name }}
         
     | 
| 34 | 
         
            +
                    run: >
         
     | 
| 35 | 
         
            +
                      pyinstaller -y -F ${{ matrix.mode }} -i "extra/DUCE-LOGO.ico" --clean --name "${{ matrix.name }}"
         
     | 
| 36 | 
         
            +
                      --add-data "base.py;."
         
     | 
| 37 | 
         
            +
                      --add-data "colors.py;."
         
     | 
| 38 | 
         
            +
                      --add-data "default-duce-${{ matrix.script }}-settings.json;."
         
     | 
| 39 | 
         
            +
                      --add-data "README.md;."
         
     | 
| 40 | 
         
            +
                      --add-data "LICENSE;."
         
     | 
| 41 | 
         
            +
                      "${{ matrix.script }}.py"
         
     | 
| 42 | 
         
            +
             
     | 
| 43 | 
         
            +
                  - name: Upload ${{ matrix.name }}.exe
         
     | 
| 44 | 
         
            +
                    uses: actions/upload-artifact@v4
         
     | 
| 45 | 
         
            +
                    with:
         
     | 
| 46 | 
         
            +
                      name: ${{ matrix.name }}.exe
         
     | 
| 47 | 
         
            +
                      path: ./dist/${{ matrix.name }}.exe
         
     | 
    	
        .github/workflows/del-old.yml
    ADDED
    
    | 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            name: 'Delete old releases'
         
     | 
| 2 | 
         
            +
            on:
         
     | 
| 3 | 
         
            +
              workflow_dispatch:
         
     | 
| 4 | 
         
            +
             
     | 
| 5 | 
         
            +
            jobs:
         
     | 
| 6 | 
         
            +
              stale:
         
     | 
| 7 | 
         
            +
                runs-on: ubuntu-latest
         
     | 
| 8 | 
         
            +
                steps:
         
     | 
| 9 | 
         
            +
                  - run: ls
         
     | 
| 10 | 
         
            +
                  #- uses: dev-drprasad/delete-older-releases@v0.2.0
         
     | 
| 11 | 
         
            +
                  #  with:
         
     | 
| 12 | 
         
            +
                  #    keep_latest: 1 
         
     | 
| 13 | 
         
            +
                  #    delete_tags: true 
         
     | 
| 14 | 
         
            +
                  #  env:
         
     | 
| 15 | 
         
            +
                  #    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         
     | 
    	
        .gitignore
    ADDED
    
    | 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            .vscode/
         
     | 
| 2 | 
         
            +
            /test
         
     | 
| 3 | 
         
            +
            /Courses
         
     | 
| 4 | 
         
            +
            /debug
         
     | 
| 5 | 
         
            +
            /build
         
     | 
| 6 | 
         
            +
            /dist
         
     | 
| 7 | 
         
            +
            /output
         
     | 
| 8 | 
         
            +
            *.pyc
         
     | 
| 9 | 
         
            +
            tempCodeRunnerFile.py
         
     | 
| 10 | 
         
            +
            DUCE-GUI-windows.spec
         
     | 
| 11 | 
         
            +
            duce.py
         
     | 
| 12 | 
         
            +
            gui-test.py
         
     | 
| 13 | 
         
            +
            duce-gui-settings.json
         
     | 
| 14 | 
         
            +
            duce-cli-settings.json
         
     | 
    	
        CHANGELOG.md
    ADDED
    
    | 
         @@ -0,0 +1,97 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            # Changelog
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
             
     | 
| 4 | 
         
            +
            ## v2.3.1
         
     | 
| 5 | 
         
            +
             
     | 
| 6 | 
         
            +
            - Fixed missing color in print
         
     | 
| 7 | 
         
            +
            - Improve update checker
         
     | 
| 8 | 
         
            +
            - Improved Already enrolled course detection
         
     | 
| 9 | 
         
            +
             
     | 
| 10 | 
         
            +
             
     | 
| 11 | 
         
            +
            ## v2.3
         
     | 
| 12 | 
         
            +
             
     | 
| 13 | 
         
            +
            - Removed getting settings from github is file not found. Default settings will be included in exe.
         
     | 
| 14 | 
         
            +
            - Changed Manual Login API
         
     | 
| 15 | 
         
            +
            - Fixed `TutorialBar`
         
     | 
| 16 | 
         
            +
            - Fixed Error for Courses that are no longer accepting new enrollments
         
     | 
| 17 | 
         
            +
            - Refactored some code
         
     | 
| 18 | 
         
            +
             
     | 
| 19 | 
         
            +
            ## v2.2
         
     | 
| 20 | 
         
            +
             
     | 
| 21 | 
         
            +
            - Fixed `CourseVania`
         
     | 
| 22 | 
         
            +
            - Refactored code
         
     | 
| 23 | 
         
            +
            - Added Course last Updated filter
         
     | 
| 24 | 
         
            +
             
     | 
| 25 | 
         
            +
            ## v2.1
         
     | 
| 26 | 
         
            +
             
     | 
| 27 | 
         
            +
            - Fixed Scrappers
         
     | 
| 28 | 
         
            +
            - Optimized some code
         
     | 
| 29 | 
         
            +
            - Fixed multiple issues
         
     | 
| 30 | 
         
            +
            - Hopefully all known errors are fixed
         
     | 
| 31 | 
         
            +
            - CLI now supports Browser Cookie Login (Can be changed in settings)
         
     | 
| 32 | 
         
            +
             
     | 
| 33 | 
         
            +
            ## v2.0
         
     | 
| 34 | 
         
            +
             
     | 
| 35 | 
         
            +
            - Fix Retrying error
         
     | 
| 36 | 
         
            +
             
     | 
| 37 | 
         
            +
            ## v1.9
         
     | 
| 38 | 
         
            +
             
     | 
| 39 | 
         
            +
            - Potential fix for Manual Login
         
     | 
| 40 | 
         
            +
            - Fixed error on encountering free course with coupons
         
     | 
| 41 | 
         
            +
            - Fixed IDownloadCoupons
         
     | 
| 42 | 
         
            +
            - Added support for Urdu and Nepali language
         
     | 
| 43 | 
         
            +
             
     | 
| 44 | 
         
            +
            ## v1.8
         
     | 
| 45 | 
         
            +
             
     | 
| 46 | 
         
            +
            - Refactored code
         
     | 
| 47 | 
         
            +
            - Fixed Course not enrolling
         
     | 
| 48 | 
         
            +
            - Fixed real discount
         
     | 
| 49 | 
         
            +
            - Fixed enext
         
     | 
| 50 | 
         
            +
            - Fixed coursevania
         
     | 
| 51 | 
         
            +
            - Fixed Manual Login
         
     | 
| 52 | 
         
            +
            - Fixed scrapers
         
     | 
| 53 | 
         
            +
            - Fixed a lot of things
         
     | 
| 54 | 
         
            +
            - Removed Colab Version because Login not possible
         
     | 
| 55 | 
         
            +
             
     | 
| 56 | 
         
            +
            ## v1.7
         
     | 
| 57 | 
         
            +
             
     | 
| 58 | 
         
            +
            - Fixed Auto-Login
         
     | 
| 59 | 
         
            +
             
     | 
| 60 | 
         
            +
            ## v1.6
         
     | 
| 61 | 
         
            +
             
     | 
| 62 | 
         
            +
            - Fixed Login issues
         
     | 
| 63 | 
         
            +
            - Fixed `CourseVania`
         
     | 
| 64 | 
         
            +
            - Fixed Enrolling
         
     | 
| 65 | 
         
            +
            - Some minor fixes
         
     | 
| 66 | 
         
            +
             
     | 
| 67 | 
         
            +
            ## v1.5
         
     | 
| 68 | 
         
            +
             
     | 
| 69 | 
         
            +
            - Fixed login problem.
         
     | 
| 70 | 
         
            +
            - Fixed my ego.
         
     | 
| 71 | 
         
            +
             
     | 
| 72 | 
         
            +
            ## v1.4
         
     | 
| 73 | 
         
            +
             
     | 
| 74 | 
         
            +
            - Added `e-next.in`
         
     | 
| 75 | 
         
            +
            - Added Discounted only filter
         
     | 
| 76 | 
         
            +
            - Hopeful fix for `Amount saved` not showing
         
     | 
| 77 | 
         
            +
            - Hopeful fix for Manual login
         
     | 
| 78 | 
         
            +
            - Fixed not saving courses to file on unexpected exit.
         
     | 
| 79 | 
         
            +
            - Simplified some logic
         
     | 
| 80 | 
         
            +
             
     | 
| 81 | 
         
            +
            ## v1.3
         
     | 
| 82 | 
         
            +
             
     | 
| 83 | 
         
            +
            - Added Save to txt file option in CLI and GUI
         
     | 
| 84 | 
         
            +
            - Fixed some logic
         
     | 
| 85 | 
         
            +
             
     | 
| 86 | 
         
            +
            ## v1.2
         
     | 
| 87 | 
         
            +
             
     | 
| 88 | 
         
            +
            - Fixed RealDiscount and CourseVania
         
     | 
| 89 | 
         
            +
             
     | 
| 90 | 
         
            +
            ## v1.1
         
     | 
| 91 | 
         
            +
             
     | 
| 92 | 
         
            +
            - Fixed RealDiscount and CourseVania
         
     | 
| 93 | 
         
            +
            - Added Russian Language filter
         
     | 
| 94 | 
         
            +
             
     | 
| 95 | 
         
            +
            ## v1.0
         
     | 
| 96 | 
         
            +
             
     | 
| 97 | 
         
            +
            - Fresh start
         
     | 
    	
        CODE_OF_CONDUCT.md
    ADDED
    
    | 
         @@ -0,0 +1,128 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            # Contributor Covenant Code of Conduct
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            ## Our Pledge
         
     | 
| 4 | 
         
            +
             
     | 
| 5 | 
         
            +
            We as members, contributors, and leaders pledge to make participation in our
         
     | 
| 6 | 
         
            +
            community a harassment-free experience for everyone, regardless of age, body
         
     | 
| 7 | 
         
            +
            size, visible or invisible disability, ethnicity, sex characteristics, gender
         
     | 
| 8 | 
         
            +
            identity and expression, level of experience, education, socio-economic status,
         
     | 
| 9 | 
         
            +
            nationality, personal appearance, race, religion, or sexual identity
         
     | 
| 10 | 
         
            +
            and orientation.
         
     | 
| 11 | 
         
            +
             
     | 
| 12 | 
         
            +
            We pledge to act and interact in ways that contribute to an open, welcoming,
         
     | 
| 13 | 
         
            +
            diverse, inclusive, and healthy community.
         
     | 
| 14 | 
         
            +
             
     | 
| 15 | 
         
            +
            ## Our Standards
         
     | 
| 16 | 
         
            +
             
     | 
| 17 | 
         
            +
            Examples of behavior that contributes to a positive environment for our
         
     | 
| 18 | 
         
            +
            community include:
         
     | 
| 19 | 
         
            +
             
     | 
| 20 | 
         
            +
            * Demonstrating empathy and kindness toward other people
         
     | 
| 21 | 
         
            +
            * Being respectful of differing opinions, viewpoints, and experiences
         
     | 
| 22 | 
         
            +
            * Giving and gracefully accepting constructive feedback
         
     | 
| 23 | 
         
            +
            * Accepting responsibility and apologizing to those affected by our mistakes,
         
     | 
| 24 | 
         
            +
              and learning from the experience
         
     | 
| 25 | 
         
            +
            * Focusing on what is best not just for us as individuals, but for the
         
     | 
| 26 | 
         
            +
              overall community
         
     | 
| 27 | 
         
            +
             
     | 
| 28 | 
         
            +
            Examples of unacceptable behavior include:
         
     | 
| 29 | 
         
            +
             
     | 
| 30 | 
         
            +
            * The use of sexualized language or imagery, and sexual attention or
         
     | 
| 31 | 
         
            +
              advances of any kind
         
     | 
| 32 | 
         
            +
            * Trolling, insulting or derogatory comments, and personal or political attacks
         
     | 
| 33 | 
         
            +
            * Public or private harassment
         
     | 
| 34 | 
         
            +
            * Publishing others' private information, such as a physical or email
         
     | 
| 35 | 
         
            +
              address, without their explicit permission
         
     | 
| 36 | 
         
            +
            * Other conduct which could reasonably be considered inappropriate in a
         
     | 
| 37 | 
         
            +
              professional setting
         
     | 
| 38 | 
         
            +
             
     | 
| 39 | 
         
            +
            ## Enforcement Responsibilities
         
     | 
| 40 | 
         
            +
             
     | 
| 41 | 
         
            +
            Community leaders are responsible for clarifying and enforcing our standards of
         
     | 
| 42 | 
         
            +
            acceptable behavior and will take appropriate and fair corrective action in
         
     | 
| 43 | 
         
            +
            response to any behavior that they deem inappropriate, threatening, offensive,
         
     | 
| 44 | 
         
            +
            or harmful.
         
     | 
| 45 | 
         
            +
             
     | 
| 46 | 
         
            +
            Community leaders have the right and responsibility to remove, edit, or reject
         
     | 
| 47 | 
         
            +
            comments, commits, code, wiki edits, issues, and other contributions that are
         
     | 
| 48 | 
         
            +
            not aligned to this Code of Conduct, and will communicate reasons for moderation
         
     | 
| 49 | 
         
            +
            decisions when appropriate.
         
     | 
| 50 | 
         
            +
             
     | 
| 51 | 
         
            +
            ## Scope
         
     | 
| 52 | 
         
            +
             
     | 
| 53 | 
         
            +
            This Code of Conduct applies within all community spaces, and also applies when
         
     | 
| 54 | 
         
            +
            an individual is officially representing the community in public spaces.
         
     | 
| 55 | 
         
            +
            Examples of representing our community include using an official e-mail address,
         
     | 
| 56 | 
         
            +
            posting via an official social media account, or acting as an appointed
         
     | 
| 57 | 
         
            +
            representative at an online or offline event.
         
     | 
| 58 | 
         
            +
             
     | 
| 59 | 
         
            +
            ## Enforcement
         
     | 
| 60 | 
         
            +
             
     | 
| 61 | 
         
            +
            Instances of abusive, harassing, or otherwise unacceptable behavior may be
         
     | 
| 62 | 
         
            +
            reported to the community leaders responsible for enforcement at
         
     | 
| 63 | 
         
            +
            techtanic@outlook.com.
         
     | 
| 64 | 
         
            +
            All complaints will be reviewed and investigated promptly and fairly.
         
     | 
| 65 | 
         
            +
             
     | 
| 66 | 
         
            +
            All community leaders are obligated to respect the privacy and security of the
         
     | 
| 67 | 
         
            +
            reporter of any incident.
         
     | 
| 68 | 
         
            +
             
     | 
| 69 | 
         
            +
            ## Enforcement Guidelines
         
     | 
| 70 | 
         
            +
             
     | 
| 71 | 
         
            +
            Community leaders will follow these Community Impact Guidelines in determining
         
     | 
| 72 | 
         
            +
            the consequences for any action they deem in violation of this Code of Conduct:
         
     | 
| 73 | 
         
            +
             
     | 
| 74 | 
         
            +
            ### 1. Correction
         
     | 
| 75 | 
         
            +
             
     | 
| 76 | 
         
            +
            **Community Impact**: Use of inappropriate language or other behavior deemed
         
     | 
| 77 | 
         
            +
            unprofessional or unwelcome in the community.
         
     | 
| 78 | 
         
            +
             
     | 
| 79 | 
         
            +
            **Consequence**: A private, written warning from community leaders, providing
         
     | 
| 80 | 
         
            +
            clarity around the nature of the violation and an explanation of why the
         
     | 
| 81 | 
         
            +
            behavior was inappropriate. A public apology may be requested.
         
     | 
| 82 | 
         
            +
             
     | 
| 83 | 
         
            +
            ### 2. Warning
         
     | 
| 84 | 
         
            +
             
     | 
| 85 | 
         
            +
            **Community Impact**: A violation through a single incident or series
         
     | 
| 86 | 
         
            +
            of actions.
         
     | 
| 87 | 
         
            +
             
     | 
| 88 | 
         
            +
            **Consequence**: A warning with consequences for continued behavior. No
         
     | 
| 89 | 
         
            +
            interaction with the people involved, including unsolicited interaction with
         
     | 
| 90 | 
         
            +
            those enforcing the Code of Conduct, for a specified period of time. This
         
     | 
| 91 | 
         
            +
            includes avoiding interactions in community spaces as well as external channels
         
     | 
| 92 | 
         
            +
            like social media. Violating these terms may lead to a temporary or
         
     | 
| 93 | 
         
            +
            permanent ban.
         
     | 
| 94 | 
         
            +
             
     | 
| 95 | 
         
            +
            ### 3. Temporary Ban
         
     | 
| 96 | 
         
            +
             
     | 
| 97 | 
         
            +
            **Community Impact**: A serious violation of community standards, including
         
     | 
| 98 | 
         
            +
            sustained inappropriate behavior.
         
     | 
| 99 | 
         
            +
             
     | 
| 100 | 
         
            +
            **Consequence**: A temporary ban from any sort of interaction or public
         
     | 
| 101 | 
         
            +
            communication with the community for a specified period of time. No public or
         
     | 
| 102 | 
         
            +
            private interaction with the people involved, including unsolicited interaction
         
     | 
| 103 | 
         
            +
            with those enforcing the Code of Conduct, is allowed during this period.
         
     | 
| 104 | 
         
            +
            Violating these terms may lead to a permanent ban.
         
     | 
| 105 | 
         
            +
             
     | 
| 106 | 
         
            +
            ### 4. Permanent Ban
         
     | 
| 107 | 
         
            +
             
     | 
| 108 | 
         
            +
            **Community Impact**: Demonstrating a pattern of violation of community
         
     | 
| 109 | 
         
            +
            standards, including sustained inappropriate behavior,  harassment of an
         
     | 
| 110 | 
         
            +
            individual, or aggression toward or disparagement of classes of individuals.
         
     | 
| 111 | 
         
            +
             
     | 
| 112 | 
         
            +
            **Consequence**: A permanent ban from any sort of public interaction within
         
     | 
| 113 | 
         
            +
            the community.
         
     | 
| 114 | 
         
            +
             
     | 
| 115 | 
         
            +
            ## Attribution
         
     | 
| 116 | 
         
            +
             
     | 
| 117 | 
         
            +
            This Code of Conduct is adapted from the [Contributor Covenant][homepage],
         
     | 
| 118 | 
         
            +
            version 2.0, available at
         
     | 
| 119 | 
         
            +
            https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
         
     | 
| 120 | 
         
            +
             
     | 
| 121 | 
         
            +
            Community Impact Guidelines were inspired by [Mozilla's code of conduct
         
     | 
| 122 | 
         
            +
            enforcement ladder](https://github.com/mozilla/diversity).
         
     | 
| 123 | 
         
            +
             
     | 
| 124 | 
         
            +
            [homepage]: https://www.contributor-covenant.org
         
     | 
| 125 | 
         
            +
             
     | 
| 126 | 
         
            +
            For answers to common questions about this code of conduct, see the FAQ at
         
     | 
| 127 | 
         
            +
            https://www.contributor-covenant.org/faq. Translations are available at
         
     | 
| 128 | 
         
            +
            https://www.contributor-covenant.org/translations.
         
     | 
    	
        CONTRIBUTING.md
    ADDED
    
    | 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            # Contributing to the project
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            I am happy to receive issues describing bug reports and feature requests! If your bug report relates to a security vulnerability, please do not file a public issue, and please instead reach out to me. I do not accept (and do not wish to receive) contributions of user-created or third-party code, including patches, pull requests, or code snippets incorporated into submitted issues. Please do not send me any such code!
         
     | 
| 4 | 
         
            +
             
     | 
| 5 | 
         
            +
            If you have a feature request, please describe the feature you would like to see, and why you think it would be useful. I am always interested in hearing about new ideas for the project.
         
     | 
| 6 | 
         
            +
             
     | 
| 7 | 
         
            +
            If you have a bug report, please describe the issue you are experiencing, and provide as much detail as possible. If you can provide a minimal example that reproduces the issue, that is even better! I will do my best to address the issue as quickly as possible.
         
     | 
| 8 | 
         
            +
             
     | 
| 9 | 
         
            +
            Thank you for your interest in contributing to the project! I appreciate your help in making the project better for everyone.
         
     | 
| 10 | 
         
            +
             
     | 
| 11 | 
         
            +
             
     | 
| 12 | 
         
            +
             
     | 
    	
        LICENSE
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            MIT License
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            Copyright (c) 2024 TECHTANIC
         
     | 
| 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.
         
     | 
    	
        base.py
    ADDED
    
    | 
         @@ -0,0 +1,1065 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            import json
         
     | 
| 2 | 
         
            +
            import os
         
     | 
| 3 | 
         
            +
            import re
         
     | 
| 4 | 
         
            +
            import sys
         
     | 
| 5 | 
         
            +
            import threading
         
     | 
| 6 | 
         
            +
            import time
         
     | 
| 7 | 
         
            +
            import traceback
         
     | 
| 8 | 
         
            +
            from datetime import datetime, timezone
         
     | 
| 9 | 
         
            +
            from decimal import Decimal
         
     | 
| 10 | 
         
            +
            from urllib.parse import parse_qs, unquote, urlparse, urlsplit, urlunparse
         
     | 
| 11 | 
         
            +
             
     | 
| 12 | 
         
            +
            import cloudscraper
         
     | 
| 13 | 
         
            +
            import requests
         
     | 
| 14 | 
         
            +
            import rookiepy
         
     | 
| 15 | 
         
            +
            from bs4 import BeautifulSoup as bs
         
     | 
| 16 | 
         
            +
             
     | 
| 17 | 
         
            +
            from colors import fb, fc, fg, flb, flg, fm, fr, fy
         
     | 
| 18 | 
         
            +
             
     | 
| 19 | 
         
            +
            VERSION = "v2.3.1"
         
     | 
| 20 | 
         
            +
             
     | 
| 21 | 
         
            +
            scraper_dict: dict = {
         
     | 
| 22 | 
         
            +
                "Udemy Freebies": "uf",
         
     | 
| 23 | 
         
            +
                "Tutorial Bar": "tb",
         
     | 
| 24 | 
         
            +
                "Real Discount": "rd",
         
     | 
| 25 | 
         
            +
                "Course Vania": "cv",
         
     | 
| 26 | 
         
            +
                "IDownloadCoupons": "idc",
         
     | 
| 27 | 
         
            +
                "E-next": "en",
         
     | 
| 28 | 
         
            +
                "Discudemy": "du",
         
     | 
| 29 | 
         
            +
            }
         
     | 
| 30 | 
         
            +
             
     | 
| 31 | 
         
            +
            LINKS = {
         
     | 
| 32 | 
         
            +
                "github": "https://github.com/techtanic/Discounted-Udemy-Course-Enroller",
         
     | 
| 33 | 
         
            +
                "support": "https://techtanic.github.io/duce/support",
         
     | 
| 34 | 
         
            +
                "discord": "https://discord.gg/wFsfhJh4Rh",
         
     | 
| 35 | 
         
            +
            }
         
     | 
| 36 | 
         
            +
             
     | 
| 37 | 
         
            +
             
     | 
| 38 | 
         
            +
            class LoginException(Exception):
         
     | 
| 39 | 
         
            +
                """Login Error
         
     | 
| 40 | 
         
            +
             
     | 
| 41 | 
         
            +
                Args:
         
     | 
| 42 | 
         
            +
                    Exception (str): Exception Reason
         
     | 
| 43 | 
         
            +
                """
         
     | 
| 44 | 
         
            +
             
     | 
| 45 | 
         
            +
                pass
         
     | 
| 46 | 
         
            +
             
     | 
| 47 | 
         
            +
             
     | 
| 48 | 
         
            +
            class RaisingThread(threading.Thread):
         
     | 
| 49 | 
         
            +
                def run(self):
         
     | 
| 50 | 
         
            +
                    self._exc = None
         
     | 
| 51 | 
         
            +
                    try:
         
     | 
| 52 | 
         
            +
                        super().run()
         
     | 
| 53 | 
         
            +
                    except Exception as e:
         
     | 
| 54 | 
         
            +
                        self._exc = e
         
     | 
| 55 | 
         
            +
             
     | 
| 56 | 
         
            +
                def join(self, timeout=None):
         
     | 
| 57 | 
         
            +
                    super().join(timeout=timeout)
         
     | 
| 58 | 
         
            +
                    if self._exc:
         
     | 
| 59 | 
         
            +
                        raise self._exc
         
     | 
| 60 | 
         
            +
             
     | 
| 61 | 
         
            +
             
     | 
| 62 | 
         
            +
            def resource_path(relative_path):
         
     | 
| 63 | 
         
            +
                if hasattr(sys, "_MEIPASS"):
         
     | 
| 64 | 
         
            +
                    return os.path.join(sys._MEIPASS, relative_path)
         
     | 
| 65 | 
         
            +
                return os.path.join(os.path.abspath("."), relative_path)
         
     | 
| 66 | 
         
            +
             
     | 
| 67 | 
         
            +
             
     | 
| 68 | 
         
            +
            class Scraper:
         
     | 
| 69 | 
         
            +
                """
         
     | 
| 70 | 
         
            +
                Scrapers: RD,TB, CV, IDC, EN, DU, UF
         
     | 
| 71 | 
         
            +
                """
         
     | 
| 72 | 
         
            +
             
     | 
| 73 | 
         
            +
                def __init__(
         
     | 
| 74 | 
         
            +
                    self,
         
     | 
| 75 | 
         
            +
                    site_to_scrape: list = list(scraper_dict.keys()),
         
     | 
| 76 | 
         
            +
                    debug: bool = False,
         
     | 
| 77 | 
         
            +
                ):
         
     | 
| 78 | 
         
            +
                    self.sites = site_to_scrape
         
     | 
| 79 | 
         
            +
                    self.debug = debug
         
     | 
| 80 | 
         
            +
                    for site in self.sites:
         
     | 
| 81 | 
         
            +
                        code_name = scraper_dict[site]
         
     | 
| 82 | 
         
            +
                        setattr(self, f"{code_name}_length", 0)
         
     | 
| 83 | 
         
            +
                        setattr(self, f"{code_name}_data", [])
         
     | 
| 84 | 
         
            +
                        setattr(self, f"{code_name}_done", False)
         
     | 
| 85 | 
         
            +
                        setattr(self, f"{code_name}_progress", 0)
         
     | 
| 86 | 
         
            +
                        setattr(self, f"{code_name}_error", "")
         
     | 
| 87 | 
         
            +
             
     | 
| 88 | 
         
            +
                def get_scraped_courses(self, target: object) -> list:
         
     | 
| 89 | 
         
            +
                    threads = []
         
     | 
| 90 | 
         
            +
                    scraped_data = {}
         
     | 
| 91 | 
         
            +
                    for site in self.sites:
         
     | 
| 92 | 
         
            +
                        t = threading.Thread(
         
     | 
| 93 | 
         
            +
                            target=target,
         
     | 
| 94 | 
         
            +
                            args=(site,),
         
     | 
| 95 | 
         
            +
                            daemon=True,
         
     | 
| 96 | 
         
            +
                        )
         
     | 
| 97 | 
         
            +
                        t.start()
         
     | 
| 98 | 
         
            +
                        threads.append(t)
         
     | 
| 99 | 
         
            +
                        time.sleep(0.2)
         
     | 
| 100 | 
         
            +
                    for t in threads:
         
     | 
| 101 | 
         
            +
                        t.join()
         
     | 
| 102 | 
         
            +
                    for site in self.sites:
         
     | 
| 103 | 
         
            +
                        scraped_data[site] = getattr(self, f"{scraper_dict[site]}_data")
         
     | 
| 104 | 
         
            +
                    return scraped_data
         
     | 
| 105 | 
         
            +
             
     | 
| 106 | 
         
            +
                def append_to_list(self, target: list, title: str, link: str):
         
     | 
| 107 | 
         
            +
                    target.append((title, link))
         
     | 
| 108 | 
         
            +
             
     | 
| 109 | 
         
            +
                def fetch_page_content(self, url: str, headers: dict = None) -> bytes:
         
     | 
| 110 | 
         
            +
                    return requests.get(url, headers=headers).content
         
     | 
| 111 | 
         
            +
             
     | 
| 112 | 
         
            +
                def parse_html(self, content: str):
         
     | 
| 113 | 
         
            +
                    return bs(content, "html5lib")
         
     | 
| 114 | 
         
            +
             
     | 
| 115 | 
         
            +
                def handle_exception(self, site_code: str):
         
     | 
| 116 | 
         
            +
                    setattr(self, f"{site_code}_error", traceback.format_exc())
         
     | 
| 117 | 
         
            +
                    setattr(self, f"{site_code}_length", -1)
         
     | 
| 118 | 
         
            +
                    setattr(self, f"{site_code}_done", True)
         
     | 
| 119 | 
         
            +
                    if self.debug:
         
     | 
| 120 | 
         
            +
                        print(getattr(self, f"{site_code}_error"))
         
     | 
| 121 | 
         
            +
             
     | 
| 122 | 
         
            +
                def cleanup_link(self, link: str) -> str:
         
     | 
| 123 | 
         
            +
                    parsed_url = urlparse(link)
         
     | 
| 124 | 
         
            +
             
     | 
| 125 | 
         
            +
                    if parsed_url.netloc == "www.udemy.com":
         
     | 
| 126 | 
         
            +
                        return link
         
     | 
| 127 | 
         
            +
             
     | 
| 128 | 
         
            +
                    if parsed_url.netloc == "click.linksynergy.com":
         
     | 
| 129 | 
         
            +
                        query_params = parse_qs(parsed_url.query)
         
     | 
| 130 | 
         
            +
             
     | 
| 131 | 
         
            +
                        if "RD_PARM1" in query_params:
         
     | 
| 132 | 
         
            +
                            return unquote(query_params["RD_PARM1"][0])
         
     | 
| 133 | 
         
            +
                        elif "murl" in query_params:
         
     | 
| 134 | 
         
            +
                            return unquote(query_params["murl"][0])
         
     | 
| 135 | 
         
            +
                        else:
         
     | 
| 136 | 
         
            +
                            return ""
         
     | 
| 137 | 
         
            +
                    raise ValueError(f"Unknown link format: {link}")
         
     | 
| 138 | 
         
            +
             
     | 
| 139 | 
         
            +
                def du(self):
         
     | 
| 140 | 
         
            +
                    try:
         
     | 
| 141 | 
         
            +
                        all_items = []
         
     | 
| 142 | 
         
            +
                        head = {
         
     | 
| 143 | 
         
            +
                            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84",
         
     | 
| 144 | 
         
            +
                            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
         
     | 
| 145 | 
         
            +
                        }
         
     | 
| 146 | 
         
            +
             
     | 
| 147 | 
         
            +
                        for page in range(1, 4):
         
     | 
| 148 | 
         
            +
                            content = self.fetch_page_content(
         
     | 
| 149 | 
         
            +
                                f"https://www.discudemy.com/all/{page}", headers=head
         
     | 
| 150 | 
         
            +
                            )
         
     | 
| 151 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 152 | 
         
            +
                            page_items = soup.find_all("a", {"class": "card-header"})
         
     | 
| 153 | 
         
            +
                            all_items.extend(page_items)
         
     | 
| 154 | 
         
            +
                        self.du_length = len(all_items)
         
     | 
| 155 | 
         
            +
                        if self.debug:
         
     | 
| 156 | 
         
            +
                            print("Length:", self.du_length)
         
     | 
| 157 | 
         
            +
                        for index, item in enumerate(all_items):
         
     | 
| 158 | 
         
            +
                            self.du_progress = index
         
     | 
| 159 | 
         
            +
                            title = item.string
         
     | 
| 160 | 
         
            +
                            url = item["href"].split("/")[-1]
         
     | 
| 161 | 
         
            +
                            content = self.fetch_page_content(
         
     | 
| 162 | 
         
            +
                                f"https://www.discudemy.com/go/{url}", headers=head
         
     | 
| 163 | 
         
            +
                            )
         
     | 
| 164 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 165 | 
         
            +
                            link = soup.find("div", {"class": "ui segment"}).a["href"]
         
     | 
| 166 | 
         
            +
                            if self.debug:
         
     | 
| 167 | 
         
            +
                                print(title, link)
         
     | 
| 168 | 
         
            +
                            self.append_to_list(self.du_data, title, link)
         
     | 
| 169 | 
         
            +
             
     | 
| 170 | 
         
            +
                    except:
         
     | 
| 171 | 
         
            +
                        self.handle_exception("du")
         
     | 
| 172 | 
         
            +
                    self.du_done = True
         
     | 
| 173 | 
         
            +
                    if self.debug:
         
     | 
| 174 | 
         
            +
                        print("Return Length:", len(self.du_data))
         
     | 
| 175 | 
         
            +
             
     | 
| 176 | 
         
            +
                def uf(self):
         
     | 
| 177 | 
         
            +
                    try:
         
     | 
| 178 | 
         
            +
                        all_items = []
         
     | 
| 179 | 
         
            +
                        for page in range(1, 4):
         
     | 
| 180 | 
         
            +
                            content = self.fetch_page_content(
         
     | 
| 181 | 
         
            +
                                f"https://www.udemyfreebies.com/free-udemy-courses/{page}"
         
     | 
| 182 | 
         
            +
                            )
         
     | 
| 183 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 184 | 
         
            +
                            page_items = soup.find_all("a", {"class": "theme-img"})
         
     | 
| 185 | 
         
            +
                            all_items.extend(page_items)
         
     | 
| 186 | 
         
            +
                        self.uf_length = len(all_items)
         
     | 
| 187 | 
         
            +
                        if self.debug:
         
     | 
| 188 | 
         
            +
                            print("Length:", self.uf_length)
         
     | 
| 189 | 
         
            +
                        for index, item in enumerate(all_items):
         
     | 
| 190 | 
         
            +
                            title = item.img["alt"]
         
     | 
| 191 | 
         
            +
                            link = requests.get(
         
     | 
| 192 | 
         
            +
                                f"https://www.udemyfreebies.com/out/{item['href'].split('/')[4]}"
         
     | 
| 193 | 
         
            +
                            ).url
         
     | 
| 194 | 
         
            +
                            self.append_to_list(self.uf_data, title, link)
         
     | 
| 195 | 
         
            +
                            self.uf_progress = index
         
     | 
| 196 | 
         
            +
             
     | 
| 197 | 
         
            +
                    except:
         
     | 
| 198 | 
         
            +
                        self.handle_exception("uf")
         
     | 
| 199 | 
         
            +
                    self.uf_done = True
         
     | 
| 200 | 
         
            +
                    if self.debug:
         
     | 
| 201 | 
         
            +
                        print("Return Length:", len(self.uf_data))
         
     | 
| 202 | 
         
            +
             
     | 
| 203 | 
         
            +
                def tb(self):
         
     | 
| 204 | 
         
            +
                    try:
         
     | 
| 205 | 
         
            +
                        all_items = []
         
     | 
| 206 | 
         
            +
             
     | 
| 207 | 
         
            +
                        for page in range(1, 5):
         
     | 
| 208 | 
         
            +
                            content = self.fetch_page_content(
         
     | 
| 209 | 
         
            +
                                f"https://www.tutorialbar.com/all-courses/page/{page}"
         
     | 
| 210 | 
         
            +
                            )
         
     | 
| 211 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 212 | 
         
            +
                            page_items = soup.find_all(
         
     | 
| 213 | 
         
            +
                                "h2", class_="mb15 mt0 font110 mobfont100 fontnormal lineheight20"
         
     | 
| 214 | 
         
            +
                            )
         
     | 
| 215 | 
         
            +
                            all_items.extend(page_items)
         
     | 
| 216 | 
         
            +
                        self.tb_length = len(all_items)
         
     | 
| 217 | 
         
            +
                        if self.debug:
         
     | 
| 218 | 
         
            +
                            print("Length:", self.tb_length)
         
     | 
| 219 | 
         
            +
             
     | 
| 220 | 
         
            +
                        for index, item in enumerate(all_items):
         
     | 
| 221 | 
         
            +
                            self.tb_progress = index
         
     | 
| 222 | 
         
            +
                            title = item.a.string
         
     | 
| 223 | 
         
            +
                            url = item.a["href"]
         
     | 
| 224 | 
         
            +
                            content = self.fetch_page_content(url)
         
     | 
| 225 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 226 | 
         
            +
                            link = soup.find("a", class_="btn_offer_block re_track_btn")["href"]
         
     | 
| 227 | 
         
            +
                            if "www.udemy.com" in link:
         
     | 
| 228 | 
         
            +
                                self.append_to_list(self.tb_data, title, link)
         
     | 
| 229 | 
         
            +
             
     | 
| 230 | 
         
            +
                    except:
         
     | 
| 231 | 
         
            +
                        self.handle_exception("tb")
         
     | 
| 232 | 
         
            +
                    self.tb_done = True
         
     | 
| 233 | 
         
            +
                    if self.debug:
         
     | 
| 234 | 
         
            +
                        print("Return Length:", len(self.tb_data))
         
     | 
| 235 | 
         
            +
             
     | 
| 236 | 
         
            +
                def rd(self):
         
     | 
| 237 | 
         
            +
                    all_items = []
         
     | 
| 238 | 
         
            +
             
     | 
| 239 | 
         
            +
                    try:
         
     | 
| 240 | 
         
            +
                        headers = {
         
     | 
| 241 | 
         
            +
                            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84",
         
     | 
| 242 | 
         
            +
                            "Host": "www.real.discount",
         
     | 
| 243 | 
         
            +
                            "Connection": "Keep-Alive",
         
     | 
| 244 | 
         
            +
                            "dnt": "1",
         
     | 
| 245 | 
         
            +
                        }
         
     | 
| 246 | 
         
            +
                        try:
         
     | 
| 247 | 
         
            +
                            r = requests.get(
         
     | 
| 248 | 
         
            +
                                "https://www.real.discount/api-web/all-courses/?store=Udemy&page=1&per_page=500&orderby=date&free=1&editorschoices=0",
         
     | 
| 249 | 
         
            +
                                headers=headers,
         
     | 
| 250 | 
         
            +
                                timeout=(10, 30),
         
     | 
| 251 | 
         
            +
                            ).json()
         
     | 
| 252 | 
         
            +
                        except requests.exceptions.Timeout:
         
     | 
| 253 | 
         
            +
                            self.rd_error = "Timeout"
         
     | 
| 254 | 
         
            +
                            self.rd_length = -1
         
     | 
| 255 | 
         
            +
                            self.rd_done = True
         
     | 
| 256 | 
         
            +
                            return
         
     | 
| 257 | 
         
            +
                        all_items.extend(r["results"])
         
     | 
| 258 | 
         
            +
             
     | 
| 259 | 
         
            +
                        self.rd_length = len(all_items)
         
     | 
| 260 | 
         
            +
                        if self.debug:
         
     | 
| 261 | 
         
            +
                            print("Length:", self.rd_length)
         
     | 
| 262 | 
         
            +
                        for index, item in enumerate(all_items):
         
     | 
| 263 | 
         
            +
                            self.rd_progress = index
         
     | 
| 264 | 
         
            +
                            title: str = item["name"]
         
     | 
| 265 | 
         
            +
                            link: str = item["url"]
         
     | 
| 266 | 
         
            +
                            link = self.cleanup_link(link)
         
     | 
| 267 | 
         
            +
                            if link:
         
     | 
| 268 | 
         
            +
                                self.append_to_list(self.rd_data, title, link)
         
     | 
| 269 | 
         
            +
             
     | 
| 270 | 
         
            +
                    except:
         
     | 
| 271 | 
         
            +
                        self.handle_exception("rd")
         
     | 
| 272 | 
         
            +
                    if self.debug:
         
     | 
| 273 | 
         
            +
                        print("Return Length:", len(self.rd_data))
         
     | 
| 274 | 
         
            +
                    self.rd_done = True
         
     | 
| 275 | 
         
            +
             
     | 
| 276 | 
         
            +
                def cv(self):
         
     | 
| 277 | 
         
            +
                    try:
         
     | 
| 278 | 
         
            +
                        content = self.fetch_page_content("https://coursevania.com/courses/")
         
     | 
| 279 | 
         
            +
                        soup = self.parse_html(content)
         
     | 
| 280 | 
         
            +
                        try:
         
     | 
| 281 | 
         
            +
                            nonce = json.loads(
         
     | 
| 282 | 
         
            +
                                re.search(
         
     | 
| 283 | 
         
            +
                                    r"var stm_lms_nonces = ({.*?});", soup.text, re.DOTALL
         
     | 
| 284 | 
         
            +
                                ).group(1)
         
     | 
| 285 | 
         
            +
                            )["load_content"]
         
     | 
| 286 | 
         
            +
                            if self.debug:
         
     | 
| 287 | 
         
            +
                                print("Nonce:", nonce)
         
     | 
| 288 | 
         
            +
                        except IndexError:
         
     | 
| 289 | 
         
            +
                            self.cv_error = "Nonce not found"
         
     | 
| 290 | 
         
            +
                            self.cv_length = -1
         
     | 
| 291 | 
         
            +
                            self.cv_done = True
         
     | 
| 292 | 
         
            +
                            return
         
     | 
| 293 | 
         
            +
                        r = requests.get(
         
     | 
| 294 | 
         
            +
                            "https://coursevania.com/wp-admin/admin-ajax.php?&template=courses/grid&args={%22posts_per_page%22:%2260%22}&action=stm_lms_load_content&nonce="
         
     | 
| 295 | 
         
            +
                            + nonce
         
     | 
| 296 | 
         
            +
                            + "&sort=date_high"
         
     | 
| 297 | 
         
            +
                        ).json()
         
     | 
| 298 | 
         
            +
             
     | 
| 299 | 
         
            +
                        soup = self.parse_html(r["content"])
         
     | 
| 300 | 
         
            +
                        page_items = soup.find_all(
         
     | 
| 301 | 
         
            +
                            "div", {"class": "stm_lms_courses__single--title"}
         
     | 
| 302 | 
         
            +
                        )
         
     | 
| 303 | 
         
            +
                        self.cv_length = len(page_items)
         
     | 
| 304 | 
         
            +
                        if self.debug:
         
     | 
| 305 | 
         
            +
                            print("Small Length:", self.cv_length)
         
     | 
| 306 | 
         
            +
                        for index, item in enumerate(page_items):
         
     | 
| 307 | 
         
            +
                            self.cv_progress = index
         
     | 
| 308 | 
         
            +
                            title = item.h5.string
         
     | 
| 309 | 
         
            +
                            content = self.fetch_page_content(item.a["href"])
         
     | 
| 310 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 311 | 
         
            +
                            link = soup.find(
         
     | 
| 312 | 
         
            +
                                "a",
         
     | 
| 313 | 
         
            +
                                {"class": "masterstudy-button-affiliate__link"},
         
     | 
| 314 | 
         
            +
                            )["href"]
         
     | 
| 315 | 
         
            +
                            self.append_to_list(self.cv_data, title, link)
         
     | 
| 316 | 
         
            +
             
     | 
| 317 | 
         
            +
                    except:
         
     | 
| 318 | 
         
            +
                        self.handle_exception("cv")
         
     | 
| 319 | 
         
            +
                    self.cv_done = True
         
     | 
| 320 | 
         
            +
                    if self.debug:
         
     | 
| 321 | 
         
            +
                        print("Return Length:", len(self.cv_data))
         
     | 
| 322 | 
         
            +
             
     | 
| 323 | 
         
            +
                def idc(self):
         
     | 
| 324 | 
         
            +
                    try:
         
     | 
| 325 | 
         
            +
                        all_items = []
         
     | 
| 326 | 
         
            +
                        for page in range(1, 5):
         
     | 
| 327 | 
         
            +
                            content = self.fetch_page_content(
         
     | 
| 328 | 
         
            +
                                f"https://idownloadcoupon.com/product-category/udemy/page/{page}"
         
     | 
| 329 | 
         
            +
                            )
         
     | 
| 330 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 331 | 
         
            +
                            page_items = soup.find_all(
         
     | 
| 332 | 
         
            +
                                "a",
         
     | 
| 333 | 
         
            +
                                attrs={
         
     | 
| 334 | 
         
            +
                                    "class": "woocommerce-LoopProduct-link woocommerce-loop-product__link"
         
     | 
| 335 | 
         
            +
                                },
         
     | 
| 336 | 
         
            +
                            )
         
     | 
| 337 | 
         
            +
                            all_items.extend(page_items)
         
     | 
| 338 | 
         
            +
                        self.idc_length = len(all_items)
         
     | 
| 339 | 
         
            +
                        if self.debug:
         
     | 
| 340 | 
         
            +
                            print("Length:", self.idc_length)
         
     | 
| 341 | 
         
            +
                        for index, item in enumerate(all_items):
         
     | 
| 342 | 
         
            +
                            self.idc_progress = index
         
     | 
| 343 | 
         
            +
                            title = item.h2.string
         
     | 
| 344 | 
         
            +
                            link_num = item["href"].split("/")[4]
         
     | 
| 345 | 
         
            +
                            if link_num == "85":
         
     | 
| 346 | 
         
            +
                                continue
         
     | 
| 347 | 
         
            +
                            link = f"https://idownloadcoupon.com/udemy/{link_num}/"
         
     | 
| 348 | 
         
            +
             
     | 
| 349 | 
         
            +
                            r = requests.get(
         
     | 
| 350 | 
         
            +
                                link,
         
     | 
| 351 | 
         
            +
                                allow_redirects=False,
         
     | 
| 352 | 
         
            +
                            )
         
     | 
| 353 | 
         
            +
                            link = unquote(r.headers["Location"])
         
     | 
| 354 | 
         
            +
                            link = self.cleanup_link(link)
         
     | 
| 355 | 
         
            +
                            self.append_to_list(self.idc_data, title, link)
         
     | 
| 356 | 
         
            +
             
     | 
| 357 | 
         
            +
                    except:
         
     | 
| 358 | 
         
            +
                        self.handle_exception("idc")
         
     | 
| 359 | 
         
            +
                    self.idc_done = True
         
     | 
| 360 | 
         
            +
                    if self.debug:
         
     | 
| 361 | 
         
            +
                        print("Return Length:", len(self.idc_data))
         
     | 
| 362 | 
         
            +
             
     | 
| 363 | 
         
            +
                def en(self):
         
     | 
| 364 | 
         
            +
                    try:
         
     | 
| 365 | 
         
            +
                        all_items = []
         
     | 
| 366 | 
         
            +
                        for page in range(1, 6):
         
     | 
| 367 | 
         
            +
                            content = self.fetch_page_content(
         
     | 
| 368 | 
         
            +
                                f"https://jobs.e-next.in/course/udemy/{page}"
         
     | 
| 369 | 
         
            +
                            )
         
     | 
| 370 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 371 | 
         
            +
                            page_items = soup.find_all(
         
     | 
| 372 | 
         
            +
                                "a", {"class": "btn btn-secondary btn-sm btn-block"}
         
     | 
| 373 | 
         
            +
                            )
         
     | 
| 374 | 
         
            +
                            all_items.extend(page_items)
         
     | 
| 375 | 
         
            +
             
     | 
| 376 | 
         
            +
                        self.en_length = len(all_items)
         
     | 
| 377 | 
         
            +
             
     | 
| 378 | 
         
            +
                        if self.debug:
         
     | 
| 379 | 
         
            +
                            print("Length:", self.en_length)
         
     | 
| 380 | 
         
            +
                        for index, item in enumerate(all_items):
         
     | 
| 381 | 
         
            +
                            self.en_progress = index
         
     | 
| 382 | 
         
            +
                            content = self.fetch_page_content(item["href"])
         
     | 
| 383 | 
         
            +
                            soup = self.parse_html(content)
         
     | 
| 384 | 
         
            +
                            title = soup.find("h3").string.strip()
         
     | 
| 385 | 
         
            +
                            link = soup.find("a", {"class": "btn btn-primary"})["href"]
         
     | 
| 386 | 
         
            +
                            self.append_to_list(self.en_data, title, link)
         
     | 
| 387 | 
         
            +
             
     | 
| 388 | 
         
            +
                    except:
         
     | 
| 389 | 
         
            +
                        self.handle_exception("en")
         
     | 
| 390 | 
         
            +
                    self.en_done = True
         
     | 
| 391 | 
         
            +
                    if self.debug:
         
     | 
| 392 | 
         
            +
                        print("Return Length:", len(self.en_data))
         
     | 
| 393 | 
         
            +
                        print(self.en_data)
         
     | 
| 394 | 
         
            +
             
     | 
| 395 | 
         
            +
             
     | 
| 396 | 
         
            +
            class Udemy:
         
     | 
| 397 | 
         
            +
                def __init__(self, interface: str, debug: bool = False):
         
     | 
| 398 | 
         
            +
                    self.interface = interface
         
     | 
| 399 | 
         
            +
                    self.client = cloudscraper.CloudScraper()
         
     | 
| 400 | 
         
            +
                    headers = {
         
     | 
| 401 | 
         
            +
                        "User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
         
     | 
| 402 | 
         
            +
                        "Accept": "application/json, text/plain, */*",
         
     | 
| 403 | 
         
            +
                        "Accept-Language": "en-GB,en;q=0.5",
         
     | 
| 404 | 
         
            +
                        "Referer": "https://www.udemy.com/",
         
     | 
| 405 | 
         
            +
                        "X-Requested-With": "XMLHttpRequest",
         
     | 
| 406 | 
         
            +
                        "DNT": "1",
         
     | 
| 407 | 
         
            +
                        "Connection": "keep-alive",
         
     | 
| 408 | 
         
            +
                        "Sec-Fetch-Dest": "empty",
         
     | 
| 409 | 
         
            +
                        "Sec-Fetch-Mode": "cors",
         
     | 
| 410 | 
         
            +
                        "Sec-Fetch-Site": "same-origin",
         
     | 
| 411 | 
         
            +
                        "Pragma": "no-cache",
         
     | 
| 412 | 
         
            +
                        "Cache-Control": "no-cache",
         
     | 
| 413 | 
         
            +
                    }
         
     | 
| 414 | 
         
            +
             
     | 
| 415 | 
         
            +
                    self.client.headers.update(headers)
         
     | 
| 416 | 
         
            +
                    self.debug = debug
         
     | 
| 417 | 
         
            +
             
     | 
| 418 | 
         
            +
                def print(self, content: str, color: str = "red", **kargs):
         
     | 
| 419 | 
         
            +
                    content = str(content)
         
     | 
| 420 | 
         
            +
                    colours_dict = {
         
     | 
| 421 | 
         
            +
                        "yellow": fy,
         
     | 
| 422 | 
         
            +
                        "red": fr,
         
     | 
| 423 | 
         
            +
                        "blue": fb,
         
     | 
| 424 | 
         
            +
                        "light blue": flb,
         
     | 
| 425 | 
         
            +
                        "green": fg,
         
     | 
| 426 | 
         
            +
                        "light green": flg,
         
     | 
| 427 | 
         
            +
                        "cyan": fc,
         
     | 
| 428 | 
         
            +
                        "magenta": fm,
         
     | 
| 429 | 
         
            +
                    }
         
     | 
| 430 | 
         
            +
                    if self.interface == "gui":
         
     | 
| 431 | 
         
            +
                        self.window["out"].print(content, text_color=color, **kargs)
         
     | 
| 432 | 
         
            +
                    else:
         
     | 
| 433 | 
         
            +
                        print(colours_dict[color] + content, **kargs)
         
     | 
| 434 | 
         
            +
             
     | 
| 435 | 
         
            +
                def get_date_from_utc(self, d: str):
         
     | 
| 436 | 
         
            +
                    utc_dt = datetime.strptime(d, "%Y-%m-%dT%H:%M:%SZ")
         
     | 
| 437 | 
         
            +
                    dt = utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
         
     | 
| 438 | 
         
            +
                    return dt.strftime("%B %d, %Y")
         
     | 
| 439 | 
         
            +
             
     | 
| 440 | 
         
            +
                def get_now_to_utc(self):
         
     | 
| 441 | 
         
            +
                    return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
         
     | 
| 442 | 
         
            +
             
     | 
| 443 | 
         
            +
                def load_settings(self):
         
     | 
| 444 | 
         
            +
                    try:
         
     | 
| 445 | 
         
            +
                        with open(f"duce-{self.interface}-settings.json") as f:
         
     | 
| 446 | 
         
            +
                            self.settings = json.load(f)
         
     | 
| 447 | 
         
            +
                    except FileNotFoundError:
         
     | 
| 448 | 
         
            +
                        with open(
         
     | 
| 449 | 
         
            +
                            resource_path(f"default-duce-{self.interface}-settings.json")
         
     | 
| 450 | 
         
            +
                        ) as f:
         
     | 
| 451 | 
         
            +
                            self.settings = json.load(f)
         
     | 
| 452 | 
         
            +
                    if (
         
     | 
| 453 | 
         
            +
                        self.interface == "cli" and "use_browser_cookies" not in self.settings
         
     | 
| 454 | 
         
            +
                    ):  # v2.1
         
     | 
| 455 | 
         
            +
                        self.settings.get("use_browser_cookies", False)
         
     | 
| 456 | 
         
            +
                    # v2.2
         
     | 
| 457 | 
         
            +
                    if "course_update_threshold_months" not in self.settings:
         
     | 
| 458 | 
         
            +
                        self.settings["course_update_threshold_months"] = 24  # 2 years
         
     | 
| 459 | 
         
            +
             
     | 
| 460 | 
         
            +
                    self.settings["languages"] = dict(
         
     | 
| 461 | 
         
            +
                        sorted(self.settings["languages"].items(), key=lambda item: item[0])
         
     | 
| 462 | 
         
            +
                    )
         
     | 
| 463 | 
         
            +
                    self.save_settings()
         
     | 
| 464 | 
         
            +
                    self.title_exclude = "\n".join(self.settings["title_exclude"])
         
     | 
| 465 | 
         
            +
                    self.instructor_exclude = "\n".join(self.settings["instructor_exclude"])
         
     | 
| 466 | 
         
            +
             
     | 
| 467 | 
         
            +
                def save_settings(self):
         
     | 
| 468 | 
         
            +
                    with open(f"duce-{self.interface}-settings.json", "w") as f:
         
     | 
| 469 | 
         
            +
                        json.dump(self.settings, f, indent=4)
         
     | 
| 470 | 
         
            +
             
     | 
| 471 | 
         
            +
                def make_cookies(self, client_id: str, access_token: str, csrf_token: str):
         
     | 
| 472 | 
         
            +
                    self.cookie_dict = dict(
         
     | 
| 473 | 
         
            +
                        client_id=client_id,
         
     | 
| 474 | 
         
            +
                        access_token=access_token,
         
     | 
| 475 | 
         
            +
                        csrf_token=csrf_token,
         
     | 
| 476 | 
         
            +
                    )
         
     | 
| 477 | 
         
            +
             
     | 
| 478 | 
         
            +
                def fetch_cookies(self):
         
     | 
| 479 | 
         
            +
                    """Gets cookies from browser
         
     | 
| 480 | 
         
            +
                    Sets cookies_dict, cookie_jar
         
     | 
| 481 | 
         
            +
                    """
         
     | 
| 482 | 
         
            +
                    cookies = rookiepy.to_cookiejar(rookiepy.load(["www.udemy.com"]))
         
     | 
| 483 | 
         
            +
                    self.cookie_dict: dict = requests.utils.dict_from_cookiejar(cookies)
         
     | 
| 484 | 
         
            +
                    self.cookie_jar = cookies
         
     | 
| 485 | 
         
            +
             
     | 
| 486 | 
         
            +
                def get_enrolled_courses(self):
         
     | 
| 487 | 
         
            +
                    """Get enrolled courses
         
     | 
| 488 | 
         
            +
                    Sets enrolled_courses
         
     | 
| 489 | 
         
            +
             
     | 
| 490 | 
         
            +
                    {slug:enrollment_time}
         
     | 
| 491 | 
         
            +
                    """
         
     | 
| 492 | 
         
            +
                    next_page = "https://www.udemy.com/api-2.0/users/me/subscribed-courses/?ordering=-enroll_time&fields[course]=enrollment_time,url&page_size=100"
         
     | 
| 493 | 
         
            +
                    courses = {}
         
     | 
| 494 | 
         
            +
                    while next_page:
         
     | 
| 495 | 
         
            +
                        r = self.client.get(
         
     | 
| 496 | 
         
            +
                            next_page,
         
     | 
| 497 | 
         
            +
                        ).json()
         
     | 
| 498 | 
         
            +
                        for course in r["results"]:
         
     | 
| 499 | 
         
            +
                            slug = course["url"].split("/")[2]
         
     | 
| 500 | 
         
            +
                            courses[slug] = course["enrollment_time"]
         
     | 
| 501 | 
         
            +
                        next_page = r["next"]
         
     | 
| 502 | 
         
            +
                    self.enrolled_courses = courses
         
     | 
| 503 | 
         
            +
             
     | 
| 504 | 
         
            +
                def compare_versions(self, version1, version2):
         
     | 
| 505 | 
         
            +
                    v1_parts = list(map(int, version1.split(".")))
         
     | 
| 506 | 
         
            +
                    v2_parts = list(map(int, version2.split(".")))
         
     | 
| 507 | 
         
            +
                    max_length = max(len(v1_parts), len(v2_parts))
         
     | 
| 508 | 
         
            +
                    v1_parts.extend([0] * (max_length - len(v1_parts)))
         
     | 
| 509 | 
         
            +
                    v2_parts.extend([0] * (max_length - len(v2_parts)))
         
     | 
| 510 | 
         
            +
             
     | 
| 511 | 
         
            +
                    for v1, v2 in zip(v1_parts, v2_parts):
         
     | 
| 512 | 
         
            +
                        if v1 < v2:
         
     | 
| 513 | 
         
            +
                            return -1
         
     | 
| 514 | 
         
            +
                        elif v1 > v2:
         
     | 
| 515 | 
         
            +
                            return 1
         
     | 
| 516 | 
         
            +
                    return 0
         
     | 
| 517 | 
         
            +
             
     | 
| 518 | 
         
            +
                def check_for_update(self) -> tuple[str, str]:
         
     | 
| 519 | 
         
            +
                    r_version = (
         
     | 
| 520 | 
         
            +
                        requests.get(
         
     | 
| 521 | 
         
            +
                            "https://api.github.com/repos/techtanic/Discounted-Udemy-Course-Enroller/releases/latest"
         
     | 
| 522 | 
         
            +
                        )
         
     | 
| 523 | 
         
            +
                        .json()["tag_name"]
         
     | 
| 524 | 
         
            +
                        .removeprefix("v")
         
     | 
| 525 | 
         
            +
                    )
         
     | 
| 526 | 
         
            +
                    c_version = VERSION.removeprefix("v")
         
     | 
| 527 | 
         
            +
             
     | 
| 528 | 
         
            +
                    comparison = self.compare_versions(c_version, r_version)
         
     | 
| 529 | 
         
            +
             
     | 
| 530 | 
         
            +
                    if comparison == -1:
         
     | 
| 531 | 
         
            +
                        return (
         
     | 
| 532 | 
         
            +
                            f"Update {r_version} Available",
         
     | 
| 533 | 
         
            +
                            f"Update {r_version} Available",
         
     | 
| 534 | 
         
            +
                        )
         
     | 
| 535 | 
         
            +
                    elif comparison == 0:
         
     | 
| 536 | 
         
            +
                        return (
         
     | 
| 537 | 
         
            +
                            f"Login {c_version}",
         
     | 
| 538 | 
         
            +
                            f"Discounted-Udemy-Course-Enroller {c_version}",
         
     | 
| 539 | 
         
            +
                        )
         
     | 
| 540 | 
         
            +
                    else:
         
     | 
| 541 | 
         
            +
                        return (
         
     | 
| 542 | 
         
            +
                            f"Dev Login {c_version}",
         
     | 
| 543 | 
         
            +
                            f"Dev Discounted-Udemy-Course-Enroller {c_version}",
         
     | 
| 544 | 
         
            +
                        )
         
     | 
| 545 | 
         
            +
             
     | 
| 546 | 
         
            +
                def manual_login(self, email: str, password: str):
         
     | 
| 547 | 
         
            +
                    """Manual Login to Udemy using email and password and sets cookies
         
     | 
| 548 | 
         
            +
                    Args:
         
     | 
| 549 | 
         
            +
                        email (str): Email
         
     | 
| 550 | 
         
            +
                        password (str): Password
         
     | 
| 551 | 
         
            +
                    Raises:
         
     | 
| 552 | 
         
            +
                        LoginException: Login Error
         
     | 
| 553 | 
         
            +
                    """
         
     | 
| 554 | 
         
            +
                    # s = cloudscraper.CloudScraper()
         
     | 
| 555 | 
         
            +
             
     | 
| 556 | 
         
            +
                    s = requests.session()
         
     | 
| 557 | 
         
            +
                    r = s.get(
         
     | 
| 558 | 
         
            +
                        "https://www.udemy.com/join/signup-popup/?locale=en_US&response_type=html&next=https%3A%2F%2Fwww.udemy.com%2Flogout%2F",
         
     | 
| 559 | 
         
            +
                        headers={"User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)"},
         
     | 
| 560 | 
         
            +
                        # headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0",
         
     | 
| 561 | 
         
            +
                        #     'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
         
     | 
| 562 | 
         
            +
                        #     'Accept-Language': 'en-US,en;q=0.5',
         
     | 
| 563 | 
         
            +
                        #     #'Accept-Encoding': 'gzip, deflate, br',
         
     | 
| 564 | 
         
            +
                        #     'DNT': '1',
         
     | 
| 565 | 
         
            +
                        #     'Connection': 'keep-alive',
         
     | 
| 566 | 
         
            +
                        #     'Upgrade-Insecure-Requests': '1',
         
     | 
| 567 | 
         
            +
                        #     'Sec-Fetch-Dest': 'document',
         
     | 
| 568 | 
         
            +
                        #     'Sec-Fetch-Mode': 'navigate',
         
     | 
| 569 | 
         
            +
                        #     'Sec-Fetch-Site': 'none',
         
     | 
| 570 | 
         
            +
                        #     'Sec-Fetch-User': '?1',
         
     | 
| 571 | 
         
            +
                        #     'Pragma': 'no-cache',
         
     | 
| 572 | 
         
            +
                        #     'Cache-Control': 'no-cache'},
         
     | 
| 573 | 
         
            +
                    )
         
     | 
| 574 | 
         
            +
                    try:
         
     | 
| 575 | 
         
            +
                        csrf_token = r.cookies["csrftoken"]
         
     | 
| 576 | 
         
            +
                    except:
         
     | 
| 577 | 
         
            +
                        if self.debug:
         
     | 
| 578 | 
         
            +
                            print(r.text)
         
     | 
| 579 | 
         
            +
                    data = {
         
     | 
| 580 | 
         
            +
                        "csrfmiddlewaretoken": csrf_token,
         
     | 
| 581 | 
         
            +
                        "locale": "en_US",
         
     | 
| 582 | 
         
            +
                        "email": email,
         
     | 
| 583 | 
         
            +
                        "password": password,
         
     | 
| 584 | 
         
            +
                    }
         
     | 
| 585 | 
         
            +
             
     | 
| 586 | 
         
            +
                    # ss = requests.session()
         
     | 
| 587 | 
         
            +
                    s.cookies.update(r.cookies)
         
     | 
| 588 | 
         
            +
                    s.headers.update(
         
     | 
| 589 | 
         
            +
                        {
         
     | 
| 590 | 
         
            +
                            "User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
         
     | 
| 591 | 
         
            +
                            "Accept": "application/json, text/plain, */*",
         
     | 
| 592 | 
         
            +
                            "Accept-Language": "en-GB,en;q=0.5",
         
     | 
| 593 | 
         
            +
                            "Referer": "https://www.udemy.com/join/login-popup/?passwordredirect=True&response_type=json",
         
     | 
| 594 | 
         
            +
                            "Origin": "https://www.udemy.com",
         
     | 
| 595 | 
         
            +
                            "DNT": "1",
         
     | 
| 596 | 
         
            +
                            "Host": "www.udemy.com",
         
     | 
| 597 | 
         
            +
                            "Connection": "keep-alive",
         
     | 
| 598 | 
         
            +
                            "Sec-Fetch-Dest": "empty",
         
     | 
| 599 | 
         
            +
                            "Sec-Fetch-Mode": "cors",
         
     | 
| 600 | 
         
            +
                            "Sec-Fetch-Site": "same-origin",
         
     | 
| 601 | 
         
            +
                            "Pragma": "no-cache",
         
     | 
| 602 | 
         
            +
                            "Cache-Control": "no-cache",
         
     | 
| 603 | 
         
            +
                        }
         
     | 
| 604 | 
         
            +
                    )
         
     | 
| 605 | 
         
            +
                    s = cloudscraper.create_scraper(sess=s)
         
     | 
| 606 | 
         
            +
                    r = s.post(
         
     | 
| 607 | 
         
            +
                        "https://www.udemy.com/join/login-popup/?passwordredirect=True&response_type=json",
         
     | 
| 608 | 
         
            +
                        data=data,
         
     | 
| 609 | 
         
            +
                        allow_redirects=False,
         
     | 
| 610 | 
         
            +
                    )
         
     | 
| 611 | 
         
            +
                    if r.text.__contains__("returnUrl"):
         
     | 
| 612 | 
         
            +
                        self.make_cookies(
         
     | 
| 613 | 
         
            +
                            r.cookies["client_id"], r.cookies["access_token"], csrf_token
         
     | 
| 614 | 
         
            +
                        )
         
     | 
| 615 | 
         
            +
                    else:
         
     | 
| 616 | 
         
            +
                        login_error = r.json()["error"]["data"]["formErrors"][0]
         
     | 
| 617 | 
         
            +
                        if login_error[0] == "Y":
         
     | 
| 618 | 
         
            +
                            raise LoginException("Too many logins per hour try later")
         
     | 
| 619 | 
         
            +
                        elif login_error[0] == "T":
         
     | 
| 620 | 
         
            +
                            raise LoginException("Email or password incorrect")
         
     | 
| 621 | 
         
            +
                        else:
         
     | 
| 622 | 
         
            +
                            raise LoginException(login_error)
         
     | 
| 623 | 
         
            +
             
     | 
| 624 | 
         
            +
                def get_session_info(self):
         
     | 
| 625 | 
         
            +
                    """Get Session info
         
     | 
| 626 | 
         
            +
                    Sets Client Session, currency and name
         
     | 
| 627 | 
         
            +
                    """
         
     | 
| 628 | 
         
            +
                    s = cloudscraper.CloudScraper()
         
     | 
| 629 | 
         
            +
                    # headers = {
         
     | 
| 630 | 
         
            +
                    #     "authorization": "Bearer " + self.cookie_dict["access_token"],
         
     | 
| 631 | 
         
            +
                    #     "accept": "application/json, text/plain, */*",
         
     | 
| 632 | 
         
            +
                    #     "x-requested-with": "XMLHttpRequest",
         
     | 
| 633 | 
         
            +
                    #     "x-forwarded-for": str(
         
     | 
| 634 | 
         
            +
                    #         ".".join(map(str, (random.randint(0, 255) for _ in range(4))))
         
     | 
| 635 | 
         
            +
                    #     ),
         
     | 
| 636 | 
         
            +
                    #     "x-udemy-authorization": "Bearer " + self.cookie_dict["access_token"],
         
     | 
| 637 | 
         
            +
                    #     "content-type": "application/json;charset=UTF-8",
         
     | 
| 638 | 
         
            +
                    #     "origin": "https://www.udemy.com",
         
     | 
| 639 | 
         
            +
                    #     "referer": "https://www.udemy.com/",
         
     | 
| 640 | 
         
            +
                    #     "dnt": "1",
         
     | 
| 641 | 
         
            +
                    #     "User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
         
     | 
| 642 | 
         
            +
                    # }
         
     | 
| 643 | 
         
            +
             
     | 
| 644 | 
         
            +
                    headers = {
         
     | 
| 645 | 
         
            +
                        "User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
         
     | 
| 646 | 
         
            +
                        "Accept": "application/json, text/plain, */*",
         
     | 
| 647 | 
         
            +
                        "Accept-Language": "en-GB,en;q=0.5",
         
     | 
| 648 | 
         
            +
                        "Referer": "https://www.udemy.com/",
         
     | 
| 649 | 
         
            +
                        "X-Requested-With": "XMLHttpRequest",
         
     | 
| 650 | 
         
            +
                        "DNT": "1",
         
     | 
| 651 | 
         
            +
                        "Connection": "keep-alive",
         
     | 
| 652 | 
         
            +
                        "Sec-Fetch-Dest": "empty",
         
     | 
| 653 | 
         
            +
                        "Sec-Fetch-Mode": "cors",
         
     | 
| 654 | 
         
            +
                        "Sec-Fetch-Site": "same-origin",
         
     | 
| 655 | 
         
            +
                        "Pragma": "no-cache",
         
     | 
| 656 | 
         
            +
                        "Cache-Control": "no-cache",
         
     | 
| 657 | 
         
            +
                    }
         
     | 
| 658 | 
         
            +
             
     | 
| 659 | 
         
            +
                    r = s.get(
         
     | 
| 660 | 
         
            +
                        "https://www.udemy.com/api-2.0/contexts/me/?header=True",
         
     | 
| 661 | 
         
            +
                        cookies=self.cookie_dict,
         
     | 
| 662 | 
         
            +
                        headers=headers,
         
     | 
| 663 | 
         
            +
                    )
         
     | 
| 664 | 
         
            +
                    r = r.json()
         
     | 
| 665 | 
         
            +
                    if self.debug:
         
     | 
| 666 | 
         
            +
                        print(r)
         
     | 
| 667 | 
         
            +
                    if not r["header"]["isLoggedIn"]:
         
     | 
| 668 | 
         
            +
                        raise LoginException("Login Failed")
         
     | 
| 669 | 
         
            +
             
     | 
| 670 | 
         
            +
                    self.display_name: str = r["header"]["user"]["display_name"]
         
     | 
| 671 | 
         
            +
                    r = s.get(
         
     | 
| 672 | 
         
            +
                        "https://www.udemy.com/api-2.0/shopping-carts/me/",
         
     | 
| 673 | 
         
            +
                        headers=headers,
         
     | 
| 674 | 
         
            +
                        cookies=self.cookie_dict,
         
     | 
| 675 | 
         
            +
                    )
         
     | 
| 676 | 
         
            +
                    r = r.json()
         
     | 
| 677 | 
         
            +
                    self.currency: str = r["user"]["credit"]["currency_code"]
         
     | 
| 678 | 
         
            +
             
     | 
| 679 | 
         
            +
                    s = cloudscraper.CloudScraper()
         
     | 
| 680 | 
         
            +
                    s.cookies.update(self.cookie_dict)
         
     | 
| 681 | 
         
            +
                    s.headers.update(headers)
         
     | 
| 682 | 
         
            +
                    s.keep_alive = False
         
     | 
| 683 | 
         
            +
                    self.client = s
         
     | 
| 684 | 
         
            +
                    self.get_enrolled_courses()
         
     | 
| 685 | 
         
            +
             
     | 
| 686 | 
         
            +
                def is_keyword_excluded(self, title: str) -> bool:
         
     | 
| 687 | 
         
            +
                    title_words = title.casefold().split()
         
     | 
| 688 | 
         
            +
                    for word in title_words:
         
     | 
| 689 | 
         
            +
                        word = word.casefold()
         
     | 
| 690 | 
         
            +
                        if word in self.title_exclude:
         
     | 
| 691 | 
         
            +
                            return True
         
     | 
| 692 | 
         
            +
                    return False
         
     | 
| 693 | 
         
            +
             
     | 
| 694 | 
         
            +
                def is_instructor_excluded(self, instructors: list) -> bool:
         
     | 
| 695 | 
         
            +
                    for instructor in instructors:
         
     | 
| 696 | 
         
            +
                        if instructor in self.settings["instructor_exclude"]:
         
     | 
| 697 | 
         
            +
                            return True
         
     | 
| 698 | 
         
            +
                    return False
         
     | 
| 699 | 
         
            +
             
     | 
| 700 | 
         
            +
                def is_course_updated(self, last_update: str | None) -> bool:
         
     | 
| 701 | 
         
            +
                    if not last_update:
         
     | 
| 702 | 
         
            +
                        return True
         
     | 
| 703 | 
         
            +
                    current_date = datetime.now()
         
     | 
| 704 | 
         
            +
                    last_update_date = datetime.strptime(last_update, "%Y-%m-%d")
         
     | 
| 705 | 
         
            +
                    # Calculate the difference in years and months
         
     | 
| 706 | 
         
            +
                    years = current_date.year - last_update_date.year
         
     | 
| 707 | 
         
            +
                    months = current_date.month - last_update_date.month
         
     | 
| 708 | 
         
            +
                    days = current_date.day - last_update_date.day
         
     | 
| 709 | 
         
            +
             
     | 
| 710 | 
         
            +
                    # Adjust the months and years if necessary
         
     | 
| 711 | 
         
            +
                    if days < 0:
         
     | 
| 712 | 
         
            +
                        months -= 1
         
     | 
| 713 | 
         
            +
             
     | 
| 714 | 
         
            +
                    if months < 0:
         
     | 
| 715 | 
         
            +
                        years -= 1
         
     | 
| 716 | 
         
            +
                        months += 12
         
     | 
| 717 | 
         
            +
             
     | 
| 718 | 
         
            +
                    # Calculate the total month difference
         
     | 
| 719 | 
         
            +
                    month_diff = years * 12 + months
         
     | 
| 720 | 
         
            +
                    return month_diff < self.settings["course_update_threshold_months"]
         
     | 
| 721 | 
         
            +
             
     | 
| 722 | 
         
            +
                def is_user_dumb(self) -> bool:
         
     | 
| 723 | 
         
            +
                    self.sites = [key for key, value in self.settings["sites"].items() if value]
         
     | 
| 724 | 
         
            +
                    self.categories = [
         
     | 
| 725 | 
         
            +
                        key for key, value in self.settings["categories"].items() if value
         
     | 
| 726 | 
         
            +
                    ]
         
     | 
| 727 | 
         
            +
                    self.languages = [
         
     | 
| 728 | 
         
            +
                        key for key, value in self.settings["languages"].items() if value
         
     | 
| 729 | 
         
            +
                    ]
         
     | 
| 730 | 
         
            +
                    self.instructor_exclude = self.settings["instructor_exclude"]
         
     | 
| 731 | 
         
            +
                    self.title_exclude = self.settings["title_exclude"]
         
     | 
| 732 | 
         
            +
                    self.min_rating = self.settings["min_rating"]
         
     | 
| 733 | 
         
            +
                    return not all([bool(self.sites), bool(self.categories), bool(self.languages)])
         
     | 
| 734 | 
         
            +
             
     | 
| 735 | 
         
            +
                def save_course(self):
         
     | 
| 736 | 
         
            +
                    if self.settings["save_txt"]:
         
     | 
| 737 | 
         
            +
                        self.txt_file.write(f"{self.title} - {self.link}\n")
         
     | 
| 738 | 
         
            +
                        self.txt_file.flush()
         
     | 
| 739 | 
         
            +
                        os.fsync(self.txt_file.fileno())
         
     | 
| 740 | 
         
            +
             
     | 
| 741 | 
         
            +
                def remove_duplicate_courses(self):
         
     | 
| 742 | 
         
            +
                    existing_links = set()
         
     | 
| 743 | 
         
            +
                    new_data = {}
         
     | 
| 744 | 
         
            +
                    for key, courses in self.scraped_data.items():
         
     | 
| 745 | 
         
            +
                        new_data[key] = []
         
     | 
| 746 | 
         
            +
                        for title, link in courses:
         
     | 
| 747 | 
         
            +
                            link = self.normalize_link(link)
         
     | 
| 748 | 
         
            +
                            if link not in existing_links:
         
     | 
| 749 | 
         
            +
                                new_data[key].append((title, link))
         
     | 
| 750 | 
         
            +
                                existing_links.add(link)
         
     | 
| 751 | 
         
            +
                    self.scraped_data = {k: v for k, v in new_data.items() if v}
         
     | 
| 752 | 
         
            +
             
     | 
| 753 | 
         
            +
                def normalize_link(self, link):
         
     | 
| 754 | 
         
            +
                    parsed_url = urlparse(link)
         
     | 
| 755 | 
         
            +
                    path = (
         
     | 
| 756 | 
         
            +
                        parsed_url.path if parsed_url.path.endswith("/") else parsed_url.path + "/"
         
     | 
| 757 | 
         
            +
                    )
         
     | 
| 758 | 
         
            +
                    return urlunparse(
         
     | 
| 759 | 
         
            +
                        (
         
     | 
| 760 | 
         
            +
                            parsed_url.scheme,
         
     | 
| 761 | 
         
            +
                            parsed_url.netloc,
         
     | 
| 762 | 
         
            +
                            path,
         
     | 
| 763 | 
         
            +
                            parsed_url.params,
         
     | 
| 764 | 
         
            +
                            parsed_url.query,
         
     | 
| 765 | 
         
            +
                            parsed_url.fragment,
         
     | 
| 766 | 
         
            +
                        )
         
     | 
| 767 | 
         
            +
                    )
         
     | 
| 768 | 
         
            +
             
     | 
| 769 | 
         
            +
                def get_course_id(self, url):
         
     | 
| 770 | 
         
            +
                    course = {
         
     | 
| 771 | 
         
            +
                        "course_id": None,
         
     | 
| 772 | 
         
            +
                        "url": url,
         
     | 
| 773 | 
         
            +
                        "is_invalid": False,
         
     | 
| 774 | 
         
            +
                        "is_free": None,
         
     | 
| 775 | 
         
            +
                        "is_excluded": None,
         
     | 
| 776 | 
         
            +
                        "retry": None,
         
     | 
| 777 | 
         
            +
                        "msg": "Report to developer",
         
     | 
| 778 | 
         
            +
                    }
         
     | 
| 779 | 
         
            +
                    url = re.sub(r"\W+$", "", unquote(url))
         
     | 
| 780 | 
         
            +
                    try:
         
     | 
| 781 | 
         
            +
                        r = self.client.get(url)
         
     | 
| 782 | 
         
            +
                    except requests.exceptions.ConnectionError:
         
     | 
| 783 | 
         
            +
                        if self.debug:
         
     | 
| 784 | 
         
            +
                            print(r.text)
         
     | 
| 785 | 
         
            +
                        course["retry"] = True
         
     | 
| 786 | 
         
            +
                        return course
         
     | 
| 787 | 
         
            +
                    course["url"] = r.url
         
     | 
| 788 | 
         
            +
                    soup = bs(r.content, "html5lib")
         
     | 
| 789 | 
         
            +
             
     | 
| 790 | 
         
            +
                    course_id = soup.find("body").get("data-clp-course-id", "invalid")
         
     | 
| 791 | 
         
            +
             
     | 
| 792 | 
         
            +
                    if course_id == "invalid":
         
     | 
| 793 | 
         
            +
                        course["is_invalid"] = True
         
     | 
| 794 | 
         
            +
                        course["msg"] = "Course ID not found: Report to developer"
         
     | 
| 795 | 
         
            +
                        return course
         
     | 
| 796 | 
         
            +
                    course["course_id"] = course_id
         
     | 
| 797 | 
         
            +
                    dma = json.loads(soup.find("body")["data-module-args"])
         
     | 
| 798 | 
         
            +
                    if self.debug:
         
     | 
| 799 | 
         
            +
                        with open("debug/dma.json", "w") as f:
         
     | 
| 800 | 
         
            +
                            json.dump(dma, f, indent=4)
         
     | 
| 801 | 
         
            +
             
     | 
| 802 | 
         
            +
                    if dma.get("view_restriction"):
         
     | 
| 803 | 
         
            +
                        course["is_invalid"] = True
         
     | 
| 804 | 
         
            +
                        course["msg"] = dma["serverSideProps"]["limitedAccess"]["errorMessage"][
         
     | 
| 805 | 
         
            +
                            "title"
         
     | 
| 806 | 
         
            +
                        ]
         
     | 
| 807 | 
         
            +
                        return course
         
     | 
| 808 | 
         
            +
             
     | 
| 809 | 
         
            +
                    course["is_free"] = not dma["serverSideProps"]["course"].get("isPaid", True)
         
     | 
| 810 | 
         
            +
                    if not self.debug and self.is_course_excluded(dma):
         
     | 
| 811 | 
         
            +
                        course["is_excluded"] = True
         
     | 
| 812 | 
         
            +
                        return course
         
     | 
| 813 | 
         
            +
             
     | 
| 814 | 
         
            +
                    return course
         
     | 
| 815 | 
         
            +
             
     | 
| 816 | 
         
            +
                def is_course_excluded(self, dma):
         
     | 
| 817 | 
         
            +
                    instructors = [
         
     | 
| 818 | 
         
            +
                        i["absolute_url"].split("/")[-2]
         
     | 
| 819 | 
         
            +
                        for i in dma["serverSideProps"]["course"]["instructors"]["instructors_info"]
         
     | 
| 820 | 
         
            +
                        if i["absolute_url"]
         
     | 
| 821 | 
         
            +
                    ]
         
     | 
| 822 | 
         
            +
                    lang = dma["serverSideProps"]["course"]["localeSimpleEnglishTitle"]
         
     | 
| 823 | 
         
            +
                    cat = dma["serverSideProps"]["topicMenu"]["breadcrumbs"][0]["title"]
         
     | 
| 824 | 
         
            +
                    rating = dma["serverSideProps"]["course"]["rating"]
         
     | 
| 825 | 
         
            +
                    last_update = dma["serverSideProps"]["course"]["lastUpdateDate"]
         
     | 
| 826 | 
         
            +
             
     | 
| 827 | 
         
            +
                    if not self.is_course_updated(last_update):
         
     | 
| 828 | 
         
            +
                        self.print(
         
     | 
| 829 | 
         
            +
                            f"Course excluded: Last updated {last_update}", color="light blue"
         
     | 
| 830 | 
         
            +
                        )
         
     | 
| 831 | 
         
            +
                    elif self.is_instructor_excluded(instructors):
         
     | 
| 832 | 
         
            +
                        self.print(f"Instructor excluded: {instructors[0]}", color="light blue")
         
     | 
| 833 | 
         
            +
                    elif self.is_keyword_excluded(self.title):
         
     | 
| 834 | 
         
            +
                        self.print("Keyword Excluded", color="light blue")
         
     | 
| 835 | 
         
            +
                    elif cat not in self.categories:
         
     | 
| 836 | 
         
            +
                        self.print(f"Category excluded: {cat}", color="light blue")
         
     | 
| 837 | 
         
            +
                    elif lang not in self.languages:
         
     | 
| 838 | 
         
            +
                        self.print(f"Language excluded: {lang}", color="light blue")
         
     | 
| 839 | 
         
            +
                    elif rating < self.min_rating:
         
     | 
| 840 | 
         
            +
                        self.print(f"Low rating: {rating}", color="light blue")
         
     | 
| 841 | 
         
            +
                    else:
         
     | 
| 842 | 
         
            +
                        return False
         
     | 
| 843 | 
         
            +
                    return True
         
     | 
| 844 | 
         
            +
             
     | 
| 845 | 
         
            +
                def extract_course_coupon(self, url):
         
     | 
| 846 | 
         
            +
                    params = parse_qs(urlsplit(url).query)
         
     | 
| 847 | 
         
            +
                    return params.get("couponCode", [False])[0]
         
     | 
| 848 | 
         
            +
             
     | 
| 849 | 
         
            +
                def check_course(self, course_id, coupon_code=None):
         
     | 
| 850 | 
         
            +
                    url = f"https://www.udemy.com/api-2.0/course-landing-components/{course_id}/me/?components=purchase"
         
     | 
| 851 | 
         
            +
                    if coupon_code:
         
     | 
| 852 | 
         
            +
                        url += f",redeem_coupon&couponCode={coupon_code}"
         
     | 
| 853 | 
         
            +
             
     | 
| 854 | 
         
            +
                    r = self.client.get(url).json()
         
     | 
| 855 | 
         
            +
                    if self.debug:
         
     | 
| 856 | 
         
            +
                        with open("test/check_course.json", "w") as f:
         
     | 
| 857 | 
         
            +
                            json.dump(r, f, indent=4)
         
     | 
| 858 | 
         
            +
                    amount = (
         
     | 
| 859 | 
         
            +
                        r.get("purchase", {})
         
     | 
| 860 | 
         
            +
                        .get("data", {})
         
     | 
| 861 | 
         
            +
                        .get("list_price", {})
         
     | 
| 862 | 
         
            +
                        .get("amount", "retry")
         
     | 
| 863 | 
         
            +
                    )
         
     | 
| 864 | 
         
            +
                    coupon_valid = False
         
     | 
| 865 | 
         
            +
             
     | 
| 866 | 
         
            +
                    if coupon_code and "redeem_coupon" in r:
         
     | 
| 867 | 
         
            +
                        discount = r["purchase"]["data"]["pricing_result"]["discount_percent"]
         
     | 
| 868 | 
         
            +
                        status = r["redeem_coupon"]["discount_attempts"][0]["status"]
         
     | 
| 869 | 
         
            +
                        coupon_valid = discount == 100 and status == "applied"
         
     | 
| 870 | 
         
            +
             
     | 
| 871 | 
         
            +
                    return Decimal(amount), coupon_valid
         
     | 
| 872 | 
         
            +
             
     | 
| 873 | 
         
            +
                def start_enrolling(self):
         
     | 
| 874 | 
         
            +
                    self.remove_duplicate_courses()
         
     | 
| 875 | 
         
            +
                    self.initialize_counters()
         
     | 
| 876 | 
         
            +
                    self.setup_txt_file()
         
     | 
| 877 | 
         
            +
             
     | 
| 878 | 
         
            +
                    total_courses = sum(len(courses) for courses in self.scraped_data.values())
         
     | 
| 879 | 
         
            +
                    previous_courses_count = 0
         
     | 
| 880 | 
         
            +
                    for site_index, (site, courses) in enumerate(self.scraped_data.items()):
         
     | 
| 881 | 
         
            +
                        self.print(f"\nSite: {site} [{len(courses)}]", color="cyan")
         
     | 
| 882 | 
         
            +
             
     | 
| 883 | 
         
            +
                        for index, (title, link) in enumerate(courses):
         
     | 
| 884 | 
         
            +
                            self.title = title
         
     | 
| 885 | 
         
            +
                            self.link = link
         
     | 
| 886 | 
         
            +
                            self.print_course_info(previous_courses_count + index, total_courses)
         
     | 
| 887 | 
         
            +
                            self.handle_course_enrollment()
         
     | 
| 888 | 
         
            +
                        previous_courses_count += len(courses)
         
     | 
| 889 | 
         
            +
             
     | 
| 890 | 
         
            +
                def initialize_counters(self):
         
     | 
| 891 | 
         
            +
                    self.successfully_enrolled_c = 0
         
     | 
| 892 | 
         
            +
                    self.already_enrolled_c = 0
         
     | 
| 893 | 
         
            +
                    self.expired_c = 0
         
     | 
| 894 | 
         
            +
                    self.excluded_c = 0
         
     | 
| 895 | 
         
            +
                    self.amount_saved_c = 0
         
     | 
| 896 | 
         
            +
             
     | 
| 897 | 
         
            +
                def setup_txt_file(self):
         
     | 
| 898 | 
         
            +
                    if self.settings["save_txt"]:
         
     | 
| 899 | 
         
            +
                        os.makedirs("Courses/", exist_ok=True)
         
     | 
| 900 | 
         
            +
                        self.txt_file = open(
         
     | 
| 901 | 
         
            +
                            f"Courses/{time.strftime('%Y-%m-%d--%H-%M')}.txt", "w", encoding="utf-8"
         
     | 
| 902 | 
         
            +
                        )
         
     | 
| 903 | 
         
            +
             
     | 
| 904 | 
         
            +
                def print_course_info(self, index, total_courses):
         
     | 
| 905 | 
         
            +
                    self.print(f"[{index + 1} / {total_courses}] ", color="magenta", end=" ")
         
     | 
| 906 | 
         
            +
                    self.print(self.title, color="yellow", end=" ")
         
     | 
| 907 | 
         
            +
                    self.print(self.link, color="blue")
         
     | 
| 908 | 
         
            +
             
     | 
| 909 | 
         
            +
                def handle_course_enrollment(self):
         
     | 
| 910 | 
         
            +
                    slug = self.link.split("/")[4]
         
     | 
| 911 | 
         
            +
             
     | 
| 912 | 
         
            +
                    if slug in self.enrolled_courses:
         
     | 
| 913 | 
         
            +
                        self.print(
         
     | 
| 914 | 
         
            +
                            f"You purchased this course on {self.get_date_from_utc(self.enrolled_courses[slug])}",
         
     | 
| 915 | 
         
            +
                            color="light blue",
         
     | 
| 916 | 
         
            +
                        )
         
     | 
| 917 | 
         
            +
                        self.already_enrolled_c += 1
         
     | 
| 918 | 
         
            +
                        return
         
     | 
| 919 | 
         
            +
             
     | 
| 920 | 
         
            +
                    course = self.get_course_id(self.link)
         
     | 
| 921 | 
         
            +
                    if course["is_invalid"]:
         
     | 
| 922 | 
         
            +
                        self.print(course["msg"], color="red")
         
     | 
| 923 | 
         
            +
                        self.excluded_c += 1
         
     | 
| 924 | 
         
            +
                    elif course["retry"]:
         
     | 
| 925 | 
         
            +
                        self.print("Retrying...", color="red")
         
     | 
| 926 | 
         
            +
                        time.sleep(1)
         
     | 
| 927 | 
         
            +
                        self.handle_course_enrollment()
         
     | 
| 928 | 
         
            +
                    elif course["is_excluded"]:
         
     | 
| 929 | 
         
            +
                        self.excluded_c += 1
         
     | 
| 930 | 
         
            +
                    elif course["is_free"]:
         
     | 
| 931 | 
         
            +
                        self.handle_free_course(course["course_id"])
         
     | 
| 932 | 
         
            +
                    elif not course["is_free"]:
         
     | 
| 933 | 
         
            +
                        self.handle_discounted_course(course["course_id"])
         
     | 
| 934 | 
         
            +
                    else:
         
     | 
| 935 | 
         
            +
                        self.print("Unknown Error: Report this link to the developer", color="red")
         
     | 
| 936 | 
         
            +
                        self.excluded_c += 1
         
     | 
| 937 | 
         
            +
             
     | 
| 938 | 
         
            +
                def handle_free_course(self, course_id):
         
     | 
| 939 | 
         
            +
                    if self.settings["discounted_only"]:
         
     | 
| 940 | 
         
            +
                        self.print("Free course excluded", color="light blue")
         
     | 
| 941 | 
         
            +
                        self.excluded_c += 1
         
     | 
| 942 | 
         
            +
                    else:
         
     | 
| 943 | 
         
            +
                        success = self.free_checkout(course_id)
         
     | 
| 944 | 
         
            +
                        if success:
         
     | 
| 945 | 
         
            +
                            self.print("Successfully Subscribed", color="green")
         
     | 
| 946 | 
         
            +
                            self.successfully_enrolled_c += 1
         
     | 
| 947 | 
         
            +
                            self.save_course()
         
     | 
| 948 | 
         
            +
                        else:
         
     | 
| 949 | 
         
            +
                            self.print(
         
     | 
| 950 | 
         
            +
                                "Unknown Error: Report this link to the developer", color="red"
         
     | 
| 951 | 
         
            +
                            )
         
     | 
| 952 | 
         
            +
                            self.expired_c += 1
         
     | 
| 953 | 
         
            +
             
     | 
| 954 | 
         
            +
                def discounted_checkout(self, coupon, course_id) -> dict:
         
     | 
| 955 | 
         
            +
                    payload = {
         
     | 
| 956 | 
         
            +
                        "checkout_environment": "Marketplace",
         
     | 
| 957 | 
         
            +
                        "checkout_event": "Submit",
         
     | 
| 958 | 
         
            +
                        "payment_info": {
         
     | 
| 959 | 
         
            +
                            "method_id": "0",
         
     | 
| 960 | 
         
            +
                            "payment_method": "free-method",
         
     | 
| 961 | 
         
            +
                            "payment_vendor": "Free",
         
     | 
| 962 | 
         
            +
                        },
         
     | 
| 963 | 
         
            +
                        "shopping_info": {
         
     | 
| 964 | 
         
            +
                            "items": [
         
     | 
| 965 | 
         
            +
                                {
         
     | 
| 966 | 
         
            +
                                    "buyable": {"id": course_id, "type": "course"},
         
     | 
| 967 | 
         
            +
                                    "discountInfo": {"code": coupon},
         
     | 
| 968 | 
         
            +
                                    "price": {"amount": 0, "currency": self.currency.upper()},
         
     | 
| 969 | 
         
            +
                                }
         
     | 
| 970 | 
         
            +
                            ],
         
     | 
| 971 | 
         
            +
                            "is_cart": False,
         
     | 
| 972 | 
         
            +
                        },
         
     | 
| 973 | 
         
            +
                    }
         
     | 
| 974 | 
         
            +
                    headers = {
         
     | 
| 975 | 
         
            +
                        "User-Agent": "okhttp/4.9.2 UdemyAndroid 8.9.2(499) (phone)",
         
     | 
| 976 | 
         
            +
                        "Accept": "application/json, text/plain, */*",
         
     | 
| 977 | 
         
            +
                        "Accept-Language": "en-US",
         
     | 
| 978 | 
         
            +
                        "Referer": f"https://www.udemy.com/payment/checkout/express/course/{course_id}/?discountCode={coupon}",
         
     | 
| 979 | 
         
            +
                        "Content-Type": "application/json",
         
     | 
| 980 | 
         
            +
                        "X-Requested-With": "XMLHttpRequest",
         
     | 
| 981 | 
         
            +
                        "x-checkout-is-mobile-app": "false",
         
     | 
| 982 | 
         
            +
                        "Origin": "https://www.udemy.com",
         
     | 
| 983 | 
         
            +
                        "DNT": "1",
         
     | 
| 984 | 
         
            +
                        "Sec-GPC": "1",
         
     | 
| 985 | 
         
            +
                        "Connection": "keep-alive",
         
     | 
| 986 | 
         
            +
                        "Sec-Fetch-Dest": "empty",
         
     | 
| 987 | 
         
            +
                        "Sec-Fetch-Mode": "cors",
         
     | 
| 988 | 
         
            +
                        "Sec-Fetch-Site": "same-origin",
         
     | 
| 989 | 
         
            +
                        "Priority": "u=0",
         
     | 
| 990 | 
         
            +
                    }
         
     | 
| 991 | 
         
            +
                    csrftoken = None
         
     | 
| 992 | 
         
            +
                    for cookie in self.client.cookies:
         
     | 
| 993 | 
         
            +
                        if cookie.name == "csrftoken":
         
     | 
| 994 | 
         
            +
                            csrftoken = cookie.value
         
     | 
| 995 | 
         
            +
                            break
         
     | 
| 996 | 
         
            +
             
     | 
| 997 | 
         
            +
                    if csrftoken:
         
     | 
| 998 | 
         
            +
                        headers["X-CSRFToken"] = csrftoken
         
     | 
| 999 | 
         
            +
                    else:
         
     | 
| 1000 | 
         
            +
                        raise ValueError("CSRF token not found")
         
     | 
| 1001 | 
         
            +
             
     | 
| 1002 | 
         
            +
                    r = self.client.post(
         
     | 
| 1003 | 
         
            +
                        "https://www.udemy.com/payment/checkout-submit/",
         
     | 
| 1004 | 
         
            +
                        json=payload,
         
     | 
| 1005 | 
         
            +
                        headers=headers,
         
     | 
| 1006 | 
         
            +
                    )
         
     | 
| 1007 | 
         
            +
                    try:
         
     | 
| 1008 | 
         
            +
                        r = r.json()
         
     | 
| 1009 | 
         
            +
                    except:
         
     | 
| 1010 | 
         
            +
                        self.print(r.text, color="red")
         
     | 
| 1011 | 
         
            +
                        self.print("Unknown Error: Report this to the developer", color="red")
         
     | 
| 1012 | 
         
            +
                    return r
         
     | 
| 1013 | 
         
            +
             
     | 
| 1014 | 
         
            +
                def free_checkout(self, course_id):
         
     | 
| 1015 | 
         
            +
                    self.client.get(f"https://www.udemy.com/course/subscribe/?courseId={course_id}")
         
     | 
| 1016 | 
         
            +
                    r = self.client.get(
         
     | 
| 1017 | 
         
            +
                        f"https://www.udemy.com/api-2.0/users/me/subscribed-courses/{course_id}/?fields%5Bcourse%5D=%40default%2Cbuyable_object_type%2Cprimary_subcategory%2Cis_private"
         
     | 
| 1018 | 
         
            +
                    ).json()
         
     | 
| 1019 | 
         
            +
                    return r.get("_class") == "course"
         
     | 
| 1020 | 
         
            +
             
     | 
| 1021 | 
         
            +
                def handle_discounted_course(self, course_id):
         
     | 
| 1022 | 
         
            +
                    coupon_code = self.extract_course_coupon(self.link)
         
     | 
| 1023 | 
         
            +
                    amount, coupon_valid = self.check_course(course_id, coupon_code)
         
     | 
| 1024 | 
         
            +
                    if amount == "retry":
         
     | 
| 1025 | 
         
            +
                        self.print("Retrying...", color="red")
         
     | 
| 1026 | 
         
            +
                        time.sleep(1)
         
     | 
| 1027 | 
         
            +
                        self.handle_discounted_course(course_id)
         
     | 
| 1028 | 
         
            +
                    elif coupon_valid:  # elif coupon_code and coupon_valid:
         
     | 
| 1029 | 
         
            +
                        self.process_coupon(course_id, coupon_code, amount)
         
     | 
| 1030 | 
         
            +
                    else:
         
     | 
| 1031 | 
         
            +
                        self.print("Coupon Expired", color="red")
         
     | 
| 1032 | 
         
            +
                        self.expired_c += 1
         
     | 
| 1033 | 
         
            +
             
     | 
| 1034 | 
         
            +
                def process_coupon(self, course_id, coupon_code, amount):
         
     | 
| 1035 | 
         
            +
                    checkout_response = self.discounted_checkout(coupon_code, course_id)
         
     | 
| 1036 | 
         
            +
                    if msg := checkout_response.get("detail"):
         
     | 
| 1037 | 
         
            +
                        self.print(msg, color="red")
         
     | 
| 1038 | 
         
            +
                        try:
         
     | 
| 1039 | 
         
            +
                            wait_time = int(re.search(r"\d+", checkout_response["detail"]).group(0))
         
     | 
| 1040 | 
         
            +
                        except:
         
     | 
| 1041 | 
         
            +
                            self.print(
         
     | 
| 1042 | 
         
            +
                                "Unknown Error: Report this link to the developer", color="red"
         
     | 
| 1043 | 
         
            +
                            )
         
     | 
| 1044 | 
         
            +
                            self.print(checkout_response, color="red")
         
     | 
| 1045 | 
         
            +
                            wait_time = 60
         
     | 
| 1046 | 
         
            +
                        time.sleep(wait_time + 1)
         
     | 
| 1047 | 
         
            +
                        self.process_coupon(course_id, coupon_code, amount)
         
     | 
| 1048 | 
         
            +
                    elif checkout_response["status"] == "succeeded":
         
     | 
| 1049 | 
         
            +
                        self.print("Successfully Enrolled To Course :)", color="green")
         
     | 
| 1050 | 
         
            +
                        self.successfully_enrolled_c += 1
         
     | 
| 1051 | 
         
            +
                        self.enrolled_courses[course_id] = self.get_now_to_utc()
         
     | 
| 1052 | 
         
            +
                        self.amount_saved_c += amount
         
     | 
| 1053 | 
         
            +
                        self.save_course()
         
     | 
| 1054 | 
         
            +
                        time.sleep(3.7)
         
     | 
| 1055 | 
         
            +
                    elif checkout_response["status"] == "failed":
         
     | 
| 1056 | 
         
            +
                        message = checkout_response["message"]
         
     | 
| 1057 | 
         
            +
                        if "item_already_subscribed" in message:
         
     | 
| 1058 | 
         
            +
                            self.print("Already Enrolled", color="light blue")
         
     | 
| 1059 | 
         
            +
                            self.already_enrolled_c += 1
         
     | 
| 1060 | 
         
            +
                        else:
         
     | 
| 1061 | 
         
            +
                            self.print("Unknown Error: Report this to the developer", color="red")
         
     | 
| 1062 | 
         
            +
                            self.print(checkout_response, color="red")
         
     | 
| 1063 | 
         
            +
                    else:
         
     | 
| 1064 | 
         
            +
                        self.print("Unknown Error: Report this to the developer", color="red")
         
     | 
| 1065 | 
         
            +
                        self.print(checkout_response, color="red")
         
     | 
    	
        cli.py
    ADDED
    
    | 
         @@ -0,0 +1,115 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            import threading
         
     | 
| 2 | 
         
            +
            import time
         
     | 
| 3 | 
         
            +
            import traceback
         
     | 
| 4 | 
         
            +
             
     | 
| 5 | 
         
            +
            from tqdm import tqdm
         
     | 
| 6 | 
         
            +
             
     | 
| 7 | 
         
            +
            from base import VERSION, LoginException, Scraper, Udemy, scraper_dict
         
     | 
| 8 | 
         
            +
            from colors import bw, by, fb, fg, fr
         
     | 
| 9 | 
         
            +
             
     | 
| 10 | 
         
            +
            # DUCE-CLI
         
     | 
| 11 | 
         
            +
             
     | 
| 12 | 
         
            +
             
     | 
| 13 | 
         
            +
            def create_scraping_thread(site: str):
         
     | 
| 14 | 
         
            +
             
     | 
| 15 | 
         
            +
                code_name = scraper_dict[site]
         
     | 
| 16 | 
         
            +
                try:
         
     | 
| 17 | 
         
            +
                    t = threading.Thread(target=getattr(scraper, code_name), daemon=True)
         
     | 
| 18 | 
         
            +
                    t.start()
         
     | 
| 19 | 
         
            +
             
     | 
| 20 | 
         
            +
                    while getattr(scraper, f"{code_name}_length") == 0:
         
     | 
| 21 | 
         
            +
                        time.sleep(0.1)  # Avoid busy waiting
         
     | 
| 22 | 
         
            +
                    if getattr(scraper, f"{code_name}_length") == -1:
         
     | 
| 23 | 
         
            +
                        raise Exception(f"Error in: {site}")
         
     | 
| 24 | 
         
            +
                    progress_bar = tqdm(
         
     | 
| 25 | 
         
            +
                        total=getattr(scraper, f"{code_name}_length"), desc=site, leave=False
         
     | 
| 26 | 
         
            +
                    )
         
     | 
| 27 | 
         
            +
                    prev_progress = -1
         
     | 
| 28 | 
         
            +
             
     | 
| 29 | 
         
            +
                    while not getattr(scraper, f"{code_name}_done"):
         
     | 
| 30 | 
         
            +
                        time.sleep(0.1)
         
     | 
| 31 | 
         
            +
                        current_progress = getattr(scraper, f"{code_name}_progress")
         
     | 
| 32 | 
         
            +
                        progress_bar.update(current_progress - prev_progress)
         
     | 
| 33 | 
         
            +
                        prev_progress = current_progress
         
     | 
| 34 | 
         
            +
             
     | 
| 35 | 
         
            +
                    progress_bar.update(getattr(scraper, f"{code_name}_length") - prev_progress)
         
     | 
| 36 | 
         
            +
             
     | 
| 37 | 
         
            +
                except Exception:
         
     | 
| 38 | 
         
            +
                    error = getattr(scraper, f"{code_name}_error", traceback.format_exc())
         
     | 
| 39 | 
         
            +
                    print(error)
         
     | 
| 40 | 
         
            +
                    print("\nError in: " + site + " " + str(VERSION))
         
     | 
| 41 | 
         
            +
             
     | 
| 42 | 
         
            +
             
     | 
| 43 | 
         
            +
            ##########################################
         
     | 
| 44 | 
         
            +
             
     | 
| 45 | 
         
            +
            udemy = Udemy("cli")
         
     | 
| 46 | 
         
            +
            udemy.load_settings()
         
     | 
| 47 | 
         
            +
            login_title, main_title = udemy.check_for_update()
         
     | 
| 48 | 
         
            +
            if login_title.__contains__("Update"):
         
     | 
| 49 | 
         
            +
                print(by + fr + login_title)
         
     | 
| 50 | 
         
            +
             
     | 
| 51 | 
         
            +
            ############## MAIN #############
         
     | 
| 52 | 
         
            +
             
     | 
| 53 | 
         
            +
            login_successful = False
         
     | 
| 54 | 
         
            +
            while not login_successful:
         
     | 
| 55 | 
         
            +
                try:
         
     | 
| 56 | 
         
            +
                    if udemy.settings["use_browser_cookies"]:
         
     | 
| 57 | 
         
            +
                        udemy.fetch_cookies()
         
     | 
| 58 | 
         
            +
                        login_method = "Browser Cookies"
         
     | 
| 59 | 
         
            +
                    elif udemy.settings["email"] and udemy.settings["password"]:
         
     | 
| 60 | 
         
            +
                        email, password = udemy.settings["email"], udemy.settings["password"]
         
     | 
| 61 | 
         
            +
                        login_method = "Saved Email and Password"
         
     | 
| 62 | 
         
            +
                    else:
         
     | 
| 63 | 
         
            +
                        email = input("Email: ")
         
     | 
| 64 | 
         
            +
                        password = input("Password: ")
         
     | 
| 65 | 
         
            +
                        login_method = "Email and Password"
         
     | 
| 66 | 
         
            +
                    print(fb + f"Trying to login using {login_method}")
         
     | 
| 67 | 
         
            +
                    if "Email" in login_method:
         
     | 
| 68 | 
         
            +
                        udemy.manual_login(email, password)
         
     | 
| 69 | 
         
            +
                    udemy.get_session_info()
         
     | 
| 70 | 
         
            +
                    if "Email" in login_method:
         
     | 
| 71 | 
         
            +
                        udemy.settings["email"], udemy.settings["password"] = email, password
         
     | 
| 72 | 
         
            +
                    login_successful = True
         
     | 
| 73 | 
         
            +
                except LoginException as e:
         
     | 
| 74 | 
         
            +
                    print(fr + str(e))
         
     | 
| 75 | 
         
            +
                    if "Browser" in login_method:
         
     | 
| 76 | 
         
            +
                        print("Cant login using cookies")
         
     | 
| 77 | 
         
            +
                        udemy.settings["use_browser_cookies"] = False
         
     | 
| 78 | 
         
            +
                    elif "Email" in login_method:
         
     | 
| 79 | 
         
            +
                        udemy.settings["email"], udemy.settings["password"] = "", ""
         
     | 
| 80 | 
         
            +
             
     | 
| 81 | 
         
            +
            udemy.save_settings()
         
     | 
| 82 | 
         
            +
             
     | 
| 83 | 
         
            +
            print(fg + f"Logged in as {udemy.display_name}")
         
     | 
| 84 | 
         
            +
            user_dumb = udemy.is_user_dumb()
         
     | 
| 85 | 
         
            +
            if user_dumb:
         
     | 
| 86 | 
         
            +
                print(bw + fr + "What do you even expect to happen!")
         
     | 
| 87 | 
         
            +
                exit()
         
     | 
| 88 | 
         
            +
            if not user_dumb:
         
     | 
| 89 | 
         
            +
                scraper = Scraper(udemy.sites)
         
     | 
| 90 | 
         
            +
            try:
         
     | 
| 91 | 
         
            +
                udemy.scraped_data = scraper.get_scraped_courses(create_scraping_thread)
         
     | 
| 92 | 
         
            +
                time.sleep(0.5)
         
     | 
| 93 | 
         
            +
                print("\n")
         
     | 
| 94 | 
         
            +
                udemy.start_enrolling()
         
     | 
| 95 | 
         
            +
             
     | 
| 96 | 
         
            +
                udemy.print(
         
     | 
| 97 | 
         
            +
                    f"\nSuccessfully Enrolled: {udemy.successfully_enrolled_c}", color="green"
         
     | 
| 98 | 
         
            +
                )
         
     | 
| 99 | 
         
            +
                udemy.print(
         
     | 
| 100 | 
         
            +
                    f"Amount Saved: {round(udemy.amount_saved_c,2)} {udemy.currency.upper()}",
         
     | 
| 101 | 
         
            +
                    color="light green",
         
     | 
| 102 | 
         
            +
                )
         
     | 
| 103 | 
         
            +
                udemy.print(f"Already Enrolled: {udemy.already_enrolled_c}", color="blue")
         
     | 
| 104 | 
         
            +
                udemy.print(f"Excluded Courses: {udemy.excluded_c}", color="yellow")
         
     | 
| 105 | 
         
            +
                udemy.print(f"Expired Courses: {udemy.expired_c}", color="red")
         
     | 
| 106 | 
         
            +
             
     | 
| 107 | 
         
            +
            except:
         
     | 
| 108 | 
         
            +
                e = traceback.format_exc()
         
     | 
| 109 | 
         
            +
                print(
         
     | 
| 110 | 
         
            +
                    (
         
     | 
| 111 | 
         
            +
                        "Error",
         
     | 
| 112 | 
         
            +
                        e + f"\n\n{udemy.link}\n{udemy.title}" + f"|:|Unknown Error {VERSION}",
         
     | 
| 113 | 
         
            +
                    )
         
     | 
| 114 | 
         
            +
                )
         
     | 
| 115 | 
         
            +
            input("Press Enter to exit...")
         
     | 
    	
        colors.py
    ADDED
    
    | 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            from colorama import init, Fore, Back, Style
         
     | 
| 2 | 
         
            +
             
     | 
| 3 | 
         
            +
            init(autoreset=True)
         
     | 
| 4 | 
         
            +
            # colors foreground text:
         
     | 
| 5 | 
         
            +
            fc = Fore.CYAN
         
     | 
| 6 | 
         
            +
            fg = Fore.GREEN
         
     | 
| 7 | 
         
            +
            fw = Fore.WHITE
         
     | 
| 8 | 
         
            +
            fr = Fore.RED
         
     | 
| 9 | 
         
            +
            fb = Fore.BLUE
         
     | 
| 10 | 
         
            +
            flb = Fore.LIGHTBLUE_EX
         
     | 
| 11 | 
         
            +
            fbl = Fore.BLACK
         
     | 
| 12 | 
         
            +
            fy = Fore.YELLOW
         
     | 
| 13 | 
         
            +
            fm = Fore.MAGENTA
         
     | 
| 14 | 
         
            +
            flg = Fore.LIGHTGREEN_EX
         
     | 
| 15 | 
         
            +
             
     | 
| 16 | 
         
            +
            # colors background text:
         
     | 
| 17 | 
         
            +
            bc = Back.CYAN
         
     | 
| 18 | 
         
            +
            bg = Back.GREEN
         
     | 
| 19 | 
         
            +
            bw = Back.WHITE
         
     | 
| 20 | 
         
            +
            br = Back.RED
         
     | 
| 21 | 
         
            +
            bb = Back.BLUE
         
     | 
| 22 | 
         
            +
            by = Back.YELLOW
         
     | 
| 23 | 
         
            +
            bm = Back.MAGENTA
         
     | 
| 24 | 
         
            +
             
     | 
| 25 | 
         
            +
            # colors style text:
         
     | 
| 26 | 
         
            +
            sd = Style.DIM
         
     | 
| 27 | 
         
            +
            sn = Style.NORMAL
         
     | 
| 28 | 
         
            +
            sb = Style.BRIGHT
         
     | 
    	
        default-duce-cli-settings.json
    ADDED
    
    | 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            {
         
     | 
| 2 | 
         
            +
                "categories": {
         
     | 
| 3 | 
         
            +
                    "Business": true,
         
     | 
| 4 | 
         
            +
                    "Design": true,
         
     | 
| 5 | 
         
            +
                    "Development": true,
         
     | 
| 6 | 
         
            +
                    "Finance & Accounting": true,
         
     | 
| 7 | 
         
            +
                    "Health & Fitness": true,
         
     | 
| 8 | 
         
            +
                    "IT & Software": true,
         
     | 
| 9 | 
         
            +
                    "Lifestyle": true,
         
     | 
| 10 | 
         
            +
                    "Marketing": true,
         
     | 
| 11 | 
         
            +
                    "Music": true,
         
     | 
| 12 | 
         
            +
                    "Office Productivity": true,
         
     | 
| 13 | 
         
            +
                    "Personal Development": true,
         
     | 
| 14 | 
         
            +
                    "Photography & Video": true,
         
     | 
| 15 | 
         
            +
                    "Teaching & Academics": true
         
     | 
| 16 | 
         
            +
                },
         
     | 
| 17 | 
         
            +
                "languages": {
         
     | 
| 18 | 
         
            +
                    "Arabic": true,
         
     | 
| 19 | 
         
            +
                    "Chinese": true,
         
     | 
| 20 | 
         
            +
                    "Dutch": true,
         
     | 
| 21 | 
         
            +
                    "English": true,
         
     | 
| 22 | 
         
            +
                    "French": true,
         
     | 
| 23 | 
         
            +
                    "German": true,
         
     | 
| 24 | 
         
            +
                    "Hindi": true,
         
     | 
| 25 | 
         
            +
                    "Indonesian": true,
         
     | 
| 26 | 
         
            +
                    "Italian": true,
         
     | 
| 27 | 
         
            +
                    "Japanese": true,
         
     | 
| 28 | 
         
            +
                    "Korean": true,
         
     | 
| 29 | 
         
            +
                    "Nepali": true,
         
     | 
| 30 | 
         
            +
                    "Polish": true,
         
     | 
| 31 | 
         
            +
                    "Portuguese": true,
         
     | 
| 32 | 
         
            +
                    "Romanian": true,
         
     | 
| 33 | 
         
            +
                    "Russian": true,
         
     | 
| 34 | 
         
            +
                    "Spanish": true,
         
     | 
| 35 | 
         
            +
                    "Thai": true,
         
     | 
| 36 | 
         
            +
                    "Turkish": true,
         
     | 
| 37 | 
         
            +
                    "Urdu": true
         
     | 
| 38 | 
         
            +
                },
         
     | 
| 39 | 
         
            +
                "sites": {
         
     | 
| 40 | 
         
            +
                    "Real Discount": true,
         
     | 
| 41 | 
         
            +
                    "Discudemy": true,
         
     | 
| 42 | 
         
            +
                    "IDownloadCoupons": true,
         
     | 
| 43 | 
         
            +
                    "Tutorial Bar": true,
         
     | 
| 44 | 
         
            +
                    "E-next": true,
         
     | 
| 45 | 
         
            +
                    "Course Vania": true,
         
     | 
| 46 | 
         
            +
                    "Udemy Freebies": true
         
     | 
| 47 | 
         
            +
                },
         
     | 
| 48 | 
         
            +
                "min_rating": 0.0,
         
     | 
| 49 | 
         
            +
                "instructor_exclude": [
         
     | 
| 50 | 
         
            +
                    "instructor-1",
         
     | 
| 51 | 
         
            +
                    "instructor-2",
         
     | 
| 52 | 
         
            +
                    "more-bad-instructor"
         
     | 
| 53 | 
         
            +
                ],
         
     | 
| 54 | 
         
            +
                "title_exclude": [
         
     | 
| 55 | 
         
            +
                    "keyword One",
         
     | 
| 56 | 
         
            +
                    "noT_cAse SenSItiVe"
         
     | 
| 57 | 
         
            +
                ],
         
     | 
| 58 | 
         
            +
                "email": "",
         
     | 
| 59 | 
         
            +
                "password": "",
         
     | 
| 60 | 
         
            +
                "save_txt": true,
         
     | 
| 61 | 
         
            +
                "discounted_only": false,
         
     | 
| 62 | 
         
            +
                "use_browser_cookies": false,
         
     | 
| 63 | 
         
            +
                "course_update_threshold_months": 24
         
     | 
| 64 | 
         
            +
            }
         
     | 
    	
        default-duce-gui-settings.json
    ADDED
    
    | 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            {
         
     | 
| 2 | 
         
            +
                "stay_logged_in": {
         
     | 
| 3 | 
         
            +
                    "auto": false,
         
     | 
| 4 | 
         
            +
                    "manual": false
         
     | 
| 5 | 
         
            +
                },
         
     | 
| 6 | 
         
            +
                "min_rating": 0.0,
         
     | 
| 7 | 
         
            +
                "title_exclude": [],
         
     | 
| 8 | 
         
            +
                "instructor_exclude": [],
         
     | 
| 9 | 
         
            +
                "languages": {
         
     | 
| 10 | 
         
            +
                    "Arabic": true,
         
     | 
| 11 | 
         
            +
                    "Chinese": true,
         
     | 
| 12 | 
         
            +
                    "Dutch": true,
         
     | 
| 13 | 
         
            +
                    "English": true,
         
     | 
| 14 | 
         
            +
                    "French": true,
         
     | 
| 15 | 
         
            +
                    "German": true,
         
     | 
| 16 | 
         
            +
                    "Hindi": true,
         
     | 
| 17 | 
         
            +
                    "Indonesian": true,
         
     | 
| 18 | 
         
            +
                    "Italian": true,
         
     | 
| 19 | 
         
            +
                    "Japanese": true,
         
     | 
| 20 | 
         
            +
                    "Korean": true,
         
     | 
| 21 | 
         
            +
                    "Nepali": true,
         
     | 
| 22 | 
         
            +
                    "Polish": true,
         
     | 
| 23 | 
         
            +
                    "Portuguese": true,
         
     | 
| 24 | 
         
            +
                    "Romanian": true,
         
     | 
| 25 | 
         
            +
                    "Russian": true,
         
     | 
| 26 | 
         
            +
                    "Spanish": true,
         
     | 
| 27 | 
         
            +
                    "Thai": true,
         
     | 
| 28 | 
         
            +
                    "Turkish": true,
         
     | 
| 29 | 
         
            +
                    "Urdu": true
         
     | 
| 30 | 
         
            +
                },
         
     | 
| 31 | 
         
            +
                "categories": {
         
     | 
| 32 | 
         
            +
                    "Business": true,
         
     | 
| 33 | 
         
            +
                    "Design": true,
         
     | 
| 34 | 
         
            +
                    "Development": true,
         
     | 
| 35 | 
         
            +
                    "Finance & Accounting": true,
         
     | 
| 36 | 
         
            +
                    "Health & Fitness": true,
         
     | 
| 37 | 
         
            +
                    "IT & Software": true,
         
     | 
| 38 | 
         
            +
                    "Lifestyle": true,
         
     | 
| 39 | 
         
            +
                    "Marketing": true,
         
     | 
| 40 | 
         
            +
                    "Music": true,
         
     | 
| 41 | 
         
            +
                    "Office Productivity": true,
         
     | 
| 42 | 
         
            +
                    "Personal Development": true,
         
     | 
| 43 | 
         
            +
                    "Photography & Video": true,
         
     | 
| 44 | 
         
            +
                    "Teaching & Academics": true
         
     | 
| 45 | 
         
            +
                },
         
     | 
| 46 | 
         
            +
                "sites": {
         
     | 
| 47 | 
         
            +
                    "Real Discount": true,
         
     | 
| 48 | 
         
            +
                    "Discudemy": true,
         
     | 
| 49 | 
         
            +
                    "IDownloadCoupons": true,
         
     | 
| 50 | 
         
            +
                    "Tutorial Bar": true,
         
     | 
| 51 | 
         
            +
                    "E-next": true,
         
     | 
| 52 | 
         
            +
                    "Course Vania": true,
         
     | 
| 53 | 
         
            +
                    "Udemy Freebies": true
         
     | 
| 54 | 
         
            +
                },
         
     | 
| 55 | 
         
            +
                "email": "",
         
     | 
| 56 | 
         
            +
                "password": "",
         
     | 
| 57 | 
         
            +
                "save_txt": false,
         
     | 
| 58 | 
         
            +
                "discounted_only": false,
         
     | 
| 59 | 
         
            +
                "course_update_threshold_months": 24
         
     | 
| 60 | 
         
            +
            }
         
     | 
    	
        extra/DUCE-LOGO.ico
    ADDED
    
    | 
											 | 
									
								
    	
        extra/DUCE-LOGO.png
    ADDED
    
    
											 
									 | 
									
								
    	
        extra/duce-gui-main.png
    ADDED
    
    
											 
									 | 
									
								
    	
        extra/promo.gif
    ADDED
    
    
											 
									 | 
									
								
    	
        gui.py
    ADDED
    
    | 
         @@ -0,0 +1,680 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            import sys
         
     | 
| 2 | 
         
            +
            import threading
         
     | 
| 3 | 
         
            +
            import time
         
     | 
| 4 | 
         
            +
            import traceback
         
     | 
| 5 | 
         
            +
            from webbrowser import open as web
         
     | 
| 6 | 
         
            +
             
     | 
| 7 | 
         
            +
            import FreeSimpleGUI as sg
         
     | 
| 8 | 
         
            +
             
     | 
| 9 | 
         
            +
            from base import LINKS, VERSION, LoginException, Scraper, Udemy, scraper_dict
         
     | 
| 10 | 
         
            +
            from images import (
         
     | 
| 11 | 
         
            +
                auto_login,
         
     | 
| 12 | 
         
            +
                back,
         
     | 
| 13 | 
         
            +
                check_mark,
         
     | 
| 14 | 
         
            +
                exit_,
         
     | 
| 15 | 
         
            +
                icon,
         
     | 
| 16 | 
         
            +
                login,
         
     | 
| 17 | 
         
            +
                logout,
         
     | 
| 18 | 
         
            +
                manual_login_,
         
     | 
| 19 | 
         
            +
                start,
         
     | 
| 20 | 
         
            +
            )
         
     | 
| 21 | 
         
            +
             
     | 
| 22 | 
         
            +
            sg.set_global_icon(icon)
         
     | 
| 23 | 
         
            +
             
     | 
| 24 | 
         
            +
            sg.change_look_and_feel("dark")
         
     | 
| 25 | 
         
            +
            sg.theme_background_color
         
     | 
| 26 | 
         
            +
            sg.set_options(
         
     | 
| 27 | 
         
            +
                button_color=(sg.theme_background_color(), sg.theme_background_color()),
         
     | 
| 28 | 
         
            +
                border_width=0,
         
     | 
| 29 | 
         
            +
                font=10,
         
     | 
| 30 | 
         
            +
            )
         
     | 
| 31 | 
         
            +
             
     | 
| 32 | 
         
            +
             
     | 
| 33 | 
         
            +
            def update_enrolled_courses():
         
     | 
| 34 | 
         
            +
                while True:
         
     | 
| 35 | 
         
            +
                    new_menu = [
         
     | 
| 36 | 
         
            +
                        ["Help", ["Support", "Github", "Discord"]],
         
     | 
| 37 | 
         
            +
                        [f"Total Courses: {len(udemy.enrolled_courses)}"],
         
     | 
| 38 | 
         
            +
                    ]
         
     | 
| 39 | 
         
            +
                    main_window.write_event_value("Update-Menu", new_menu)
         
     | 
| 40 | 
         
            +
                    time.sleep(10)
         
     | 
| 41 | 
         
            +
             
     | 
| 42 | 
         
            +
             
     | 
| 43 | 
         
            +
            def create_scraping_thread(site: str):
         
     | 
| 44 | 
         
            +
                code_name = scraper_dict[site]
         
     | 
| 45 | 
         
            +
                main_window[f"i{site}"].update(visible=False)
         
     | 
| 46 | 
         
            +
                main_window[f"p{site}"].update(0, visible=True)
         
     | 
| 47 | 
         
            +
             
     | 
| 48 | 
         
            +
                try:
         
     | 
| 49 | 
         
            +
                    threading.Thread(target=getattr(scraper, code_name), daemon=True).start()
         
     | 
| 50 | 
         
            +
                    while getattr(scraper, f"{code_name}_length") == 0:
         
     | 
| 51 | 
         
            +
                        time.sleep(0.1)  # Avoid busy waiting
         
     | 
| 52 | 
         
            +
                    if getattr(scraper, f"{code_name}_length") == -1:
         
     | 
| 53 | 
         
            +
                        raise Exception(f"Error in: {site}")
         
     | 
| 54 | 
         
            +
             
     | 
| 55 | 
         
            +
                    main_window[f"p{site}"].update(0, max=getattr(scraper, f"{code_name}_length"))
         
     | 
| 56 | 
         
            +
                    while not getattr(scraper, f"{code_name}_done") and not getattr(
         
     | 
| 57 | 
         
            +
                        scraper, f"{code_name}_error"
         
     | 
| 58 | 
         
            +
                    ):
         
     | 
| 59 | 
         
            +
                        main_window[f"p{site}"].update(
         
     | 
| 60 | 
         
            +
                            getattr(scraper, f"{code_name}_progress") + 1
         
     | 
| 61 | 
         
            +
                        )
         
     | 
| 62 | 
         
            +
                        time.sleep(0.1)  # Update every 0.1 seconds
         
     | 
| 63 | 
         
            +
             
     | 
| 64 | 
         
            +
                    if getattr(scraper, f"{code_name}_error"):
         
     | 
| 65 | 
         
            +
                        raise Exception(f"Error in: {site}")
         
     | 
| 66 | 
         
            +
                except Exception:
         
     | 
| 67 | 
         
            +
                    error_message = getattr(scraper, f"{code_name}_error", "Unknown Error")
         
     | 
| 68 | 
         
            +
                    main_window.write_event_value(
         
     | 
| 69 | 
         
            +
                        "Error", f"{error_message}|:|Unknown Error in: {site} {VERSION}"
         
     | 
| 70 | 
         
            +
                    )
         
     | 
| 71 | 
         
            +
                finally:
         
     | 
| 72 | 
         
            +
                    main_window[f"p{site}"].update(0, visible=False)
         
     | 
| 73 | 
         
            +
                    main_window[f"i{site}"].update(visible=True)
         
     | 
| 74 | 
         
            +
             
     | 
| 75 | 
         
            +
             
     | 
| 76 | 
         
            +
            ##########################################
         
     | 
| 77 | 
         
            +
             
     | 
| 78 | 
         
            +
             
     | 
| 79 | 
         
            +
            def scrape():
         
     | 
| 80 | 
         
            +
                try:
         
     | 
| 81 | 
         
            +
                    for site in udemy.sites:
         
     | 
| 82 | 
         
            +
                        main_window[f"pcol{site}"].update(visible=True)
         
     | 
| 83 | 
         
            +
                    main_window["main_col"].update(visible=False)
         
     | 
| 84 | 
         
            +
                    main_window["scrape_col"].update(visible=True)
         
     | 
| 85 | 
         
            +
                    udemy.scraped_data = scraper.get_scraped_courses(create_scraping_thread)
         
     | 
| 86 | 
         
            +
                    main_window["scrape_col"].update(visible=False)
         
     | 
| 87 | 
         
            +
                    main_window["output_col"].update(visible=True)
         
     | 
| 88 | 
         
            +
                    # ------------------------------------------
         
     | 
| 89 | 
         
            +
                    udemy.start_enrolling()
         
     | 
| 90 | 
         
            +
                    main_window["output_col"].Update(visible=False)
         
     | 
| 91 | 
         
            +
             
     | 
| 92 | 
         
            +
                    main_window["done_col"].update(visible=True)
         
     | 
| 93 | 
         
            +
             
     | 
| 94 | 
         
            +
                    main_window["se_c"].update(
         
     | 
| 95 | 
         
            +
                        value=f"Successfully Enrolled: {udemy.successfully_enrolled_c}"
         
     | 
| 96 | 
         
            +
                    )
         
     | 
| 97 | 
         
            +
                    main_window["as_c"].update(
         
     | 
| 98 | 
         
            +
                        value=f"Amount Saved: {round(udemy.amount_saved_c,2)} {udemy.currency.upper()}"
         
     | 
| 99 | 
         
            +
                    )
         
     | 
| 100 | 
         
            +
                    main_window["ae_c"].update(
         
     | 
| 101 | 
         
            +
                        value=f"Already Enrolled: {udemy.already_enrolled_c}"
         
     | 
| 102 | 
         
            +
                    )
         
     | 
| 103 | 
         
            +
                    main_window["e_c"].update(value=f"Expired Courses: {udemy.expired_c}")
         
     | 
| 104 | 
         
            +
                    main_window["ex_c"].update(value=f"Excluded Courses: {udemy.excluded_c}")
         
     | 
| 105 | 
         
            +
             
     | 
| 106 | 
         
            +
                except Exception:
         
     | 
| 107 | 
         
            +
                    e = traceback.format_exc()
         
     | 
| 108 | 
         
            +
                    main_window.write_event_value(
         
     | 
| 109 | 
         
            +
                        "Error",
         
     | 
| 110 | 
         
            +
                        f"{e}\n\nVersion:{VERSION}\nLink:{getattr(udemy, 'link', 'None')}\nTitle:{getattr(udemy, 'title','None')}|:|Error g100",
         
     | 
| 111 | 
         
            +
                    )
         
     | 
| 112 | 
         
            +
             
     | 
| 113 | 
         
            +
             
     | 
| 114 | 
         
            +
            #################################
         
     | 
| 115 | 
         
            +
            udemy = Udemy("gui")
         
     | 
| 116 | 
         
            +
            udemy.load_settings()
         
     | 
| 117 | 
         
            +
            login_title, main_title = udemy.check_for_update()
         
     | 
| 118 | 
         
            +
             
     | 
| 119 | 
         
            +
             
     | 
| 120 | 
         
            +
            menu = [["Help", ["Support", "Github", "Discord"]]]
         
     | 
| 121 | 
         
            +
             
     | 
| 122 | 
         
            +
            login_error = False
         
     | 
| 123 | 
         
            +
             
     | 
| 124 | 
         
            +
            try:
         
     | 
| 125 | 
         
            +
                if udemy.settings["stay_logged_in"]["auto"]:
         
     | 
| 126 | 
         
            +
                    udemy.fetch_cookies()
         
     | 
| 127 | 
         
            +
             
     | 
| 128 | 
         
            +
                elif udemy.settings["stay_logged_in"]["manual"]:
         
     | 
| 129 | 
         
            +
                    udemy.manual_login(udemy.settings["email"], udemy.settings["password"])
         
     | 
| 130 | 
         
            +
                else:
         
     | 
| 131 | 
         
            +
                    raise LoginException("No Saved Login Found")
         
     | 
| 132 | 
         
            +
                udemy.get_session_info()
         
     | 
| 133 | 
         
            +
            except LoginException:
         
     | 
| 134 | 
         
            +
                login_error = True
         
     | 
| 135 | 
         
            +
            # if (
         
     | 
| 136 | 
         
            +
            #     not udemy.settings["stay_logged_in"]["auto"]
         
     | 
| 137 | 
         
            +
            #     and not udemy.settings["stay_logged_in"]["manual"]
         
     | 
| 138 | 
         
            +
            # ) or login_error:
         
     | 
| 139 | 
         
            +
            if login_error:
         
     | 
| 140 | 
         
            +
                c1 = [
         
     | 
| 141 | 
         
            +
                    [
         
     | 
| 142 | 
         
            +
                        sg.Button(key="a_login", image_data=auto_login),
         
     | 
| 143 | 
         
            +
                        sg.T(""),
         
     | 
| 144 | 
         
            +
                        sg.B(key="m_login", image_data=manual_login_),
         
     | 
| 145 | 
         
            +
                    ],
         
     | 
| 146 | 
         
            +
                    [
         
     | 
| 147 | 
         
            +
                        sg.Checkbox(
         
     | 
| 148 | 
         
            +
                            "Stay logged-in",
         
     | 
| 149 | 
         
            +
                            default=udemy.settings["stay_logged_in"]["auto"],
         
     | 
| 150 | 
         
            +
                            key="sli_a",
         
     | 
| 151 | 
         
            +
                        )
         
     | 
| 152 | 
         
            +
                    ],
         
     | 
| 153 | 
         
            +
                ]
         
     | 
| 154 | 
         
            +
                c2 = [
         
     | 
| 155 | 
         
            +
                    [
         
     | 
| 156 | 
         
            +
                        sg.T("Email"),
         
     | 
| 157 | 
         
            +
                        sg.InputText(
         
     | 
| 158 | 
         
            +
                            default_text=udemy.settings["email"],
         
     | 
| 159 | 
         
            +
                            key="email",
         
     | 
| 160 | 
         
            +
                            size=(20, 1),
         
     | 
| 161 | 
         
            +
                            pad=(5, 5),
         
     | 
| 162 | 
         
            +
                        ),
         
     | 
| 163 | 
         
            +
                    ],
         
     | 
| 164 | 
         
            +
                    [
         
     | 
| 165 | 
         
            +
                        sg.T("Password"),
         
     | 
| 166 | 
         
            +
                        sg.InputText(
         
     | 
| 167 | 
         
            +
                            default_text=udemy.settings["password"],
         
     | 
| 168 | 
         
            +
                            key="password",
         
     | 
| 169 | 
         
            +
                            size=(20, 1),
         
     | 
| 170 | 
         
            +
                            pad=(5, 5),
         
     | 
| 171 | 
         
            +
                            password_char="*",
         
     | 
| 172 | 
         
            +
                        ),
         
     | 
| 173 | 
         
            +
                    ],
         
     | 
| 174 | 
         
            +
                    [
         
     | 
| 175 | 
         
            +
                        sg.Checkbox(
         
     | 
| 176 | 
         
            +
                            "Stay logged-in",
         
     | 
| 177 | 
         
            +
                            default=udemy.settings["stay_logged_in"]["manual"],
         
     | 
| 178 | 
         
            +
                            key="sli_m",
         
     | 
| 179 | 
         
            +
                        )
         
     | 
| 180 | 
         
            +
                    ],
         
     | 
| 181 | 
         
            +
                    [
         
     | 
| 182 | 
         
            +
                        sg.B(key="Back", image_data=back),
         
     | 
| 183 | 
         
            +
                        sg.T("                     "),
         
     | 
| 184 | 
         
            +
                        sg.B(key="Login", image_data=login),
         
     | 
| 185 | 
         
            +
                    ],
         
     | 
| 186 | 
         
            +
                ]
         
     | 
| 187 | 
         
            +
             
     | 
| 188 | 
         
            +
                login_layout = [
         
     | 
| 189 | 
         
            +
                    [sg.Menu(menu)],
         
     | 
| 190 | 
         
            +
                    [sg.Column(c1, key="col1"), sg.Column(c2, visible=False, key="col2")],
         
     | 
| 191 | 
         
            +
                ]
         
     | 
| 192 | 
         
            +
             
     | 
| 193 | 
         
            +
                login_window = sg.Window(login_title, login_layout, finalize=True)
         
     | 
| 194 | 
         
            +
                login_window.bind("a", "a_login")
         
     | 
| 195 | 
         
            +
                login_window.bind("m", "m_login")
         
     | 
| 196 | 
         
            +
                while True:
         
     | 
| 197 | 
         
            +
                    event, values = login_window.read()
         
     | 
| 198 | 
         
            +
             
     | 
| 199 | 
         
            +
                    if event in (None,):
         
     | 
| 200 | 
         
            +
                        login_window.close()
         
     | 
| 201 | 
         
            +
                        sys.exit()
         
     | 
| 202 | 
         
            +
             
     | 
| 203 | 
         
            +
                    elif event == "a_login" and not login_window["a_login"].Disabled:
         
     | 
| 204 | 
         
            +
                        login_window["a_login"].update(disabled=True)
         
     | 
| 205 | 
         
            +
                        login_window.refresh()
         
     | 
| 206 | 
         
            +
                        try:
         
     | 
| 207 | 
         
            +
                            udemy.fetch_cookies()
         
     | 
| 208 | 
         
            +
                            try:
         
     | 
| 209 | 
         
            +
                                udemy.get_session_info()
         
     | 
| 210 | 
         
            +
                                udemy.settings["stay_logged_in"]["auto"] = values["sli_a"]
         
     | 
| 211 | 
         
            +
                                udemy.save_settings()
         
     | 
| 212 | 
         
            +
                                login_window.close()
         
     | 
| 213 | 
         
            +
                                break
         
     | 
| 214 | 
         
            +
                            except Exception:
         
     | 
| 215 | 
         
            +
                                e = traceback.format_exc()
         
     | 
| 216 | 
         
            +
                                print(e)
         
     | 
| 217 | 
         
            +
                                sg.popup_auto_close(
         
     | 
| 218 | 
         
            +
                                    "Make sure you are logged in to udemy.com in chrome browser",
         
     | 
| 219 | 
         
            +
                                    title="Error",
         
     | 
| 220 | 
         
            +
                                    auto_close_duration=3,
         
     | 
| 221 | 
         
            +
                                    no_titlebar=True,
         
     | 
| 222 | 
         
            +
                                )
         
     | 
| 223 | 
         
            +
             
     | 
| 224 | 
         
            +
                        except Exception:
         
     | 
| 225 | 
         
            +
                            e = traceback.format_exc()
         
     | 
| 226 | 
         
            +
                            sg.popup_scrolled(e, title=f"Unknown Error {VERSION}")
         
     | 
| 227 | 
         
            +
             
     | 
| 228 | 
         
            +
                        login_window["a_login"].update(disabled=False)
         
     | 
| 229 | 
         
            +
                    elif event == "m_login":
         
     | 
| 230 | 
         
            +
                        login_window["col1"].update(visible=False)
         
     | 
| 231 | 
         
            +
                        login_window["col2"].update(visible=True)
         
     | 
| 232 | 
         
            +
             
     | 
| 233 | 
         
            +
                        login_window["email"].update(value=udemy.settings["email"])
         
     | 
| 234 | 
         
            +
                        login_window["password"].update(value=udemy.settings["password"])
         
     | 
| 235 | 
         
            +
             
     | 
| 236 | 
         
            +
                    elif event == "Github":
         
     | 
| 237 | 
         
            +
                        web(LINKS["github"])
         
     | 
| 238 | 
         
            +
             
     | 
| 239 | 
         
            +
                    elif event == "Support":
         
     | 
| 240 | 
         
            +
                        web(LINKS["support"])
         
     | 
| 241 | 
         
            +
             
     | 
| 242 | 
         
            +
                    elif event == "Discord":
         
     | 
| 243 | 
         
            +
                        web(LINKS["discord"])
         
     | 
| 244 | 
         
            +
             
     | 
| 245 | 
         
            +
                    elif event == "Back":
         
     | 
| 246 | 
         
            +
                        login_window["col1"].update(visible=True)
         
     | 
| 247 | 
         
            +
                        login_window["col2"].update(visible=False)
         
     | 
| 248 | 
         
            +
             
     | 
| 249 | 
         
            +
                    elif event == "Login":
         
     | 
| 250 | 
         
            +
                        udemy.settings["email"] = values["email"]
         
     | 
| 251 | 
         
            +
                        udemy.settings["password"] = values["password"]
         
     | 
| 252 | 
         
            +
                        try:
         
     | 
| 253 | 
         
            +
                            try:
         
     | 
| 254 | 
         
            +
                                udemy.manual_login(
         
     | 
| 255 | 
         
            +
                                    udemy.settings["email"], udemy.settings["password"]
         
     | 
| 256 | 
         
            +
                                )
         
     | 
| 257 | 
         
            +
                                udemy.get_session_info()
         
     | 
| 258 | 
         
            +
                                udemy.settings["stay_logged_in"]["manual"] = values["sli_m"]
         
     | 
| 259 | 
         
            +
                                udemy.save_settings()
         
     | 
| 260 | 
         
            +
                                login_window.close()
         
     | 
| 261 | 
         
            +
                                break
         
     | 
| 262 | 
         
            +
                            except LoginException as e:
         
     | 
| 263 | 
         
            +
                                sg.popup_auto_close(
         
     | 
| 264 | 
         
            +
                                    e,
         
     | 
| 265 | 
         
            +
                                    title="Error",
         
     | 
| 266 | 
         
            +
                                    auto_close_duration=3,
         
     | 
| 267 | 
         
            +
                                    no_titlebar=True,
         
     | 
| 268 | 
         
            +
                                )
         
     | 
| 269 | 
         
            +
                        except Exception:
         
     | 
| 270 | 
         
            +
                            e = traceback.format_exc()
         
     | 
| 271 | 
         
            +
                            sg.popup_scrolled(e, title=f"Unknown Error {VERSION}")
         
     | 
| 272 | 
         
            +
             
     | 
| 273 | 
         
            +
            checkbox_lo = []
         
     | 
| 274 | 
         
            +
            for key in udemy.settings["sites"]:
         
     | 
| 275 | 
         
            +
                checkbox_lo.append(
         
     | 
| 276 | 
         
            +
                    [sg.Checkbox(key, key=key, default=udemy.settings["sites"][key], size=(18, 1))]
         
     | 
| 277 | 
         
            +
                )
         
     | 
| 278 | 
         
            +
             
     | 
| 279 | 
         
            +
            categories_lo = []
         
     | 
| 280 | 
         
            +
            categories_k = list(udemy.settings["categories"].keys())
         
     | 
| 281 | 
         
            +
            categories_v = list(udemy.settings["categories"].values())
         
     | 
| 282 | 
         
            +
            for index, _ in enumerate(udemy.settings["categories"]):
         
     | 
| 283 | 
         
            +
                if index % 3 == 0:
         
     | 
| 284 | 
         
            +
                    try:
         
     | 
| 285 | 
         
            +
                        categories_lo.append(
         
     | 
| 286 | 
         
            +
                            [
         
     | 
| 287 | 
         
            +
                                sg.Checkbox(
         
     | 
| 288 | 
         
            +
                                    categories_k[index],
         
     | 
| 289 | 
         
            +
                                    default=categories_v[index],
         
     | 
| 290 | 
         
            +
                                    key=categories_k[index],
         
     | 
| 291 | 
         
            +
                                    size=(18, 1),
         
     | 
| 292 | 
         
            +
                                ),
         
     | 
| 293 | 
         
            +
                                sg.Checkbox(
         
     | 
| 294 | 
         
            +
                                    categories_k[index + 1],
         
     | 
| 295 | 
         
            +
                                    default=categories_v[index + 1],
         
     | 
| 296 | 
         
            +
                                    key=categories_k[index + 1],
         
     | 
| 297 | 
         
            +
                                    size=(18, 1),
         
     | 
| 298 | 
         
            +
                                ),
         
     | 
| 299 | 
         
            +
                                sg.Checkbox(
         
     | 
| 300 | 
         
            +
                                    categories_k[index + 2],
         
     | 
| 301 | 
         
            +
                                    default=categories_v[index + 2],
         
     | 
| 302 | 
         
            +
                                    key=categories_k[index + 2],
         
     | 
| 303 | 
         
            +
                                    size=(18, 1),
         
     | 
| 304 | 
         
            +
                                ),
         
     | 
| 305 | 
         
            +
                            ]
         
     | 
| 306 | 
         
            +
                        )
         
     | 
| 307 | 
         
            +
                    except IndexError:
         
     | 
| 308 | 
         
            +
                        categories_lo.append(
         
     | 
| 309 | 
         
            +
                            [
         
     | 
| 310 | 
         
            +
                                sg.Checkbox(
         
     | 
| 311 | 
         
            +
                                    categories_k[index],
         
     | 
| 312 | 
         
            +
                                    default=categories_v[index],
         
     | 
| 313 | 
         
            +
                                    key=categories_k[index],
         
     | 
| 314 | 
         
            +
                                    size=(18, 1),
         
     | 
| 315 | 
         
            +
                                )
         
     | 
| 316 | 
         
            +
                            ]
         
     | 
| 317 | 
         
            +
                        )
         
     | 
| 318 | 
         
            +
             
     | 
| 319 | 
         
            +
            languages_lo = []
         
     | 
| 320 | 
         
            +
            languages_k = list(udemy.settings["languages"].keys())
         
     | 
| 321 | 
         
            +
            languages_v = list(udemy.settings["languages"].values())
         
     | 
| 322 | 
         
            +
            for index, _ in enumerate(udemy.settings["languages"]):
         
     | 
| 323 | 
         
            +
                if index % 3 == 0:
         
     | 
| 324 | 
         
            +
                    try:
         
     | 
| 325 | 
         
            +
                        languages_lo.append(
         
     | 
| 326 | 
         
            +
                            [
         
     | 
| 327 | 
         
            +
                                sg.Checkbox(
         
     | 
| 328 | 
         
            +
                                    languages_k[index],
         
     | 
| 329 | 
         
            +
                                    default=languages_v[index],
         
     | 
| 330 | 
         
            +
                                    key=languages_k[index],
         
     | 
| 331 | 
         
            +
                                    size=(10, 1),
         
     | 
| 332 | 
         
            +
                                ),
         
     | 
| 333 | 
         
            +
                                sg.Checkbox(
         
     | 
| 334 | 
         
            +
                                    languages_k[index + 1],
         
     | 
| 335 | 
         
            +
                                    default=languages_v[index + 1],
         
     | 
| 336 | 
         
            +
                                    key=languages_k[index + 1],
         
     | 
| 337 | 
         
            +
                                    size=(10, 1),
         
     | 
| 338 | 
         
            +
                                ),
         
     | 
| 339 | 
         
            +
                                sg.Checkbox(
         
     | 
| 340 | 
         
            +
                                    languages_k[index + 2],
         
     | 
| 341 | 
         
            +
                                    default=languages_v[index + 2],
         
     | 
| 342 | 
         
            +
                                    key=languages_k[index + 2],
         
     | 
| 343 | 
         
            +
                                    size=(10, 1),
         
     | 
| 344 | 
         
            +
                                ),
         
     | 
| 345 | 
         
            +
                            ]
         
     | 
| 346 | 
         
            +
                        )
         
     | 
| 347 | 
         
            +
             
     | 
| 348 | 
         
            +
                    except IndexError:
         
     | 
| 349 | 
         
            +
                        languages_lo.append(
         
     | 
| 350 | 
         
            +
                            [
         
     | 
| 351 | 
         
            +
                                sg.Checkbox(
         
     | 
| 352 | 
         
            +
                                    languages_k[index],
         
     | 
| 353 | 
         
            +
                                    default=languages_v[index],
         
     | 
| 354 | 
         
            +
                                    key=languages_k[index],
         
     | 
| 355 | 
         
            +
                                    size=(10, 1),
         
     | 
| 356 | 
         
            +
                                ),
         
     | 
| 357 | 
         
            +
                                sg.Checkbox(
         
     | 
| 358 | 
         
            +
                                    languages_k[index + 1],
         
     | 
| 359 | 
         
            +
                                    default=languages_v[index + 1],
         
     | 
| 360 | 
         
            +
                                    key=languages_k[index + 1],
         
     | 
| 361 | 
         
            +
                                    size=(10, 1),
         
     | 
| 362 | 
         
            +
                                ),
         
     | 
| 363 | 
         
            +
                            ]
         
     | 
| 364 | 
         
            +
                        )
         
     | 
| 365 | 
         
            +
             
     | 
| 366 | 
         
            +
            main_tab = [
         
     | 
| 367 | 
         
            +
                [
         
     | 
| 368 | 
         
            +
                    sg.Frame(
         
     | 
| 369 | 
         
            +
                        "Websites",
         
     | 
| 370 | 
         
            +
                        checkbox_lo,
         
     | 
| 371 | 
         
            +
                        "#4deeea",
         
     | 
| 372 | 
         
            +
                        border_width=4,
         
     | 
| 373 | 
         
            +
                        title_location="n",
         
     | 
| 374 | 
         
            +
                        key="fcb",
         
     | 
| 375 | 
         
            +
                    ),
         
     | 
| 376 | 
         
            +
                    sg.Frame(
         
     | 
| 377 | 
         
            +
                        "Language",
         
     | 
| 378 | 
         
            +
                        languages_lo,
         
     | 
| 379 | 
         
            +
                        "#4deeea",
         
     | 
| 380 | 
         
            +
                        border_width=4,
         
     | 
| 381 | 
         
            +
                        title_location="n",
         
     | 
| 382 | 
         
            +
                        key="fl",
         
     | 
| 383 | 
         
            +
                    ),
         
     | 
| 384 | 
         
            +
                ],
         
     | 
| 385 | 
         
            +
                [
         
     | 
| 386 | 
         
            +
                    sg.Frame(
         
     | 
| 387 | 
         
            +
                        "Category",
         
     | 
| 388 | 
         
            +
                        categories_lo,
         
     | 
| 389 | 
         
            +
                        "#4deeea",
         
     | 
| 390 | 
         
            +
                        border_width=4,
         
     | 
| 391 | 
         
            +
                        title_location="n",
         
     | 
| 392 | 
         
            +
                        key="fc",
         
     | 
| 393 | 
         
            +
                    )
         
     | 
| 394 | 
         
            +
                ],
         
     | 
| 395 | 
         
            +
            ]
         
     | 
| 396 | 
         
            +
             
     | 
| 397 | 
         
            +
            instructor_ex_lo = [
         
     | 
| 398 | 
         
            +
                [
         
     | 
| 399 | 
         
            +
                    sg.Multiline(
         
     | 
| 400 | 
         
            +
                        default_text=udemy.instructor_exclude,
         
     | 
| 401 | 
         
            +
                        key="instructor_exclude",
         
     | 
| 402 | 
         
            +
                        size=(15, 10),
         
     | 
| 403 | 
         
            +
                    )
         
     | 
| 404 | 
         
            +
                ],
         
     | 
| 405 | 
         
            +
                [sg.Text("Paste instructor(s)\nusername in new lines")],
         
     | 
| 406 | 
         
            +
            ]
         
     | 
| 407 | 
         
            +
            title_ex_lo = [
         
     | 
| 408 | 
         
            +
                [
         
     | 
| 409 | 
         
            +
                    sg.Multiline(
         
     | 
| 410 | 
         
            +
                        default_text=udemy.title_exclude, key="title_exclude", size=(20, 10)
         
     | 
| 411 | 
         
            +
                    )
         
     | 
| 412 | 
         
            +
                ],
         
     | 
| 413 | 
         
            +
                [sg.Text("Keywords in new lines\nNot cAsE sensitive")],
         
     | 
| 414 | 
         
            +
            ]
         
     | 
| 415 | 
         
            +
             
     | 
| 416 | 
         
            +
            rating_lo = [
         
     | 
| 417 | 
         
            +
                [
         
     | 
| 418 | 
         
            +
                    sg.Spin(
         
     | 
| 419 | 
         
            +
                        [i * 0.5 for i in range(11)],
         
     | 
| 420 | 
         
            +
                        initial_value=udemy.settings["min_rating"],
         
     | 
| 421 | 
         
            +
                        key="min_rating",
         
     | 
| 422 | 
         
            +
                    ),
         
     | 
| 423 | 
         
            +
                    sg.Text("0.0 <-> 5.0"),
         
     | 
| 424 | 
         
            +
                ]
         
     | 
| 425 | 
         
            +
            ]
         
     | 
| 426 | 
         
            +
             
     | 
| 427 | 
         
            +
            courses_last_updated_lo = [
         
     | 
| 428 | 
         
            +
                [
         
     | 
| 429 | 
         
            +
                    sg.Text("Past"),
         
     | 
| 430 | 
         
            +
                    sg.Spin(
         
     | 
| 431 | 
         
            +
                        [i for i in range(1, 48)],
         
     | 
| 432 | 
         
            +
                        initial_value=udemy.settings["course_update_threshold_months"],
         
     | 
| 433 | 
         
            +
                        key="course_update_threshold_months",
         
     | 
| 434 | 
         
            +
                    ),
         
     | 
| 435 | 
         
            +
                    sg.Text("Month(s)"),
         
     | 
| 436 | 
         
            +
                ]
         
     | 
| 437 | 
         
            +
            ]
         
     | 
| 438 | 
         
            +
             
     | 
| 439 | 
         
            +
             
     | 
| 440 | 
         
            +
            advanced_tab = [
         
     | 
| 441 | 
         
            +
                [
         
     | 
| 442 | 
         
            +
                    sg.Frame(
         
     | 
| 443 | 
         
            +
                        "Exclude Instructor",
         
     | 
| 444 | 
         
            +
                        instructor_ex_lo,
         
     | 
| 445 | 
         
            +
                        "#4deeea",
         
     | 
| 446 | 
         
            +
                        border_width=4,
         
     | 
| 447 | 
         
            +
                        title_location="n",
         
     | 
| 448 | 
         
            +
                    ),
         
     | 
| 449 | 
         
            +
                    sg.Frame(
         
     | 
| 450 | 
         
            +
                        "Title Keyword Exclusion",
         
     | 
| 451 | 
         
            +
                        title_ex_lo,
         
     | 
| 452 | 
         
            +
                        "#4deeea",
         
     | 
| 453 | 
         
            +
                        border_width=4,
         
     | 
| 454 | 
         
            +
                        title_location="n",
         
     | 
| 455 | 
         
            +
                    ),
         
     | 
| 456 | 
         
            +
                ],
         
     | 
| 457 | 
         
            +
                [
         
     | 
| 458 | 
         
            +
                    sg.Frame(
         
     | 
| 459 | 
         
            +
                        "Minimum Rating",
         
     | 
| 460 | 
         
            +
                        rating_lo,
         
     | 
| 461 | 
         
            +
                        "#4deeea",
         
     | 
| 462 | 
         
            +
                        border_width=4,
         
     | 
| 463 | 
         
            +
                        title_location="n",
         
     | 
| 464 | 
         
            +
                        key="f_min_rating",
         
     | 
| 465 | 
         
            +
                        font=25,
         
     | 
| 466 | 
         
            +
                    ),
         
     | 
| 467 | 
         
            +
                    sg.Frame(
         
     | 
| 468 | 
         
            +
                        "Course Last Updated",
         
     | 
| 469 | 
         
            +
                        courses_last_updated_lo,
         
     | 
| 470 | 
         
            +
                        "#4deeea",
         
     | 
| 471 | 
         
            +
                        border_width=4,
         
     | 
| 472 | 
         
            +
                        title_location="n",
         
     | 
| 473 | 
         
            +
                        key="f_course_last_updated",
         
     | 
| 474 | 
         
            +
                        font=25,
         
     | 
| 475 | 
         
            +
                    ),
         
     | 
| 476 | 
         
            +
                ],
         
     | 
| 477 | 
         
            +
                [
         
     | 
| 478 | 
         
            +
                    sg.Checkbox(
         
     | 
| 479 | 
         
            +
                        "Save enrolled courses in txt",
         
     | 
| 480 | 
         
            +
                        key="save_txt",
         
     | 
| 481 | 
         
            +
                        default=udemy.settings["save_txt"],
         
     | 
| 482 | 
         
            +
                    )
         
     | 
| 483 | 
         
            +
                ],
         
     | 
| 484 | 
         
            +
                [
         
     | 
| 485 | 
         
            +
                    sg.Checkbox(
         
     | 
| 486 | 
         
            +
                        "Enrol in Discounted courses only",
         
     | 
| 487 | 
         
            +
                        key="discounted_only",
         
     | 
| 488 | 
         
            +
                        default=udemy.settings["discounted_only"],
         
     | 
| 489 | 
         
            +
                    )
         
     | 
| 490 | 
         
            +
                ],
         
     | 
| 491 | 
         
            +
            ]
         
     | 
| 492 | 
         
            +
             
     | 
| 493 | 
         
            +
             
     | 
| 494 | 
         
            +
            scrape_col = []
         
     | 
| 495 | 
         
            +
            for key in udemy.settings["sites"]:
         
     | 
| 496 | 
         
            +
                scrape_col.append(
         
     | 
| 497 | 
         
            +
                    [
         
     | 
| 498 | 
         
            +
                        sg.pin(
         
     | 
| 499 | 
         
            +
                            sg.Column(
         
     | 
| 500 | 
         
            +
                                [
         
     | 
| 501 | 
         
            +
                                    [
         
     | 
| 502 | 
         
            +
                                        sg.Text(key, size=(12, 1)),
         
     | 
| 503 | 
         
            +
                                        sg.ProgressBar(
         
     | 
| 504 | 
         
            +
                                            3,
         
     | 
| 505 | 
         
            +
                                            orientation="h",
         
     | 
| 506 | 
         
            +
                                            key=f"p{key}",
         
     | 
| 507 | 
         
            +
                                            bar_color=("#1c6fba", "#000000"),
         
     | 
| 508 | 
         
            +
                                            border_width=1,
         
     | 
| 509 | 
         
            +
                                            size=(20, 20),
         
     | 
| 510 | 
         
            +
                                        ),
         
     | 
| 511 | 
         
            +
                                        sg.Image(data=check_mark, visible=False, key=f"i{key}"),
         
     | 
| 512 | 
         
            +
                                    ]
         
     | 
| 513 | 
         
            +
                                ],
         
     | 
| 514 | 
         
            +
                                key=f"pcol{key}",
         
     | 
| 515 | 
         
            +
                                visible=False,
         
     | 
| 516 | 
         
            +
                            )
         
     | 
| 517 | 
         
            +
                        )
         
     | 
| 518 | 
         
            +
                    ]
         
     | 
| 519 | 
         
            +
                )
         
     | 
| 520 | 
         
            +
             
     | 
| 521 | 
         
            +
            output_col = [
         
     | 
| 522 | 
         
            +
                [sg.Text("Output")],
         
     | 
| 523 | 
         
            +
                [sg.Multiline(size=(69, 12), key="out", autoscroll=False, disabled=True)],
         
     | 
| 524 | 
         
            +
                # [
         
     | 
| 525 | 
         
            +
                #     sg.ProgressBar(
         
     | 
| 526 | 
         
            +
                #         3,
         
     | 
| 527 | 
         
            +
                #         orientation="h",
         
     | 
| 528 | 
         
            +
                #         key="pout",
         
     | 
| 529 | 
         
            +
                #         bar_color=("#1c6fba", "#000000"),
         
     | 
| 530 | 
         
            +
                #         border_width=1,
         
     | 
| 531 | 
         
            +
                #         size=(46, 20),
         
     | 
| 532 | 
         
            +
                #     )
         
     | 
| 533 | 
         
            +
                # ],
         
     | 
| 534 | 
         
            +
            ]
         
     | 
| 535 | 
         
            +
             
     | 
| 536 | 
         
            +
            done_col = [
         
     | 
| 537 | 
         
            +
                [sg.Text("       Stats", text_color="#FFD700")],
         
     | 
| 538 | 
         
            +
                [
         
     | 
| 539 | 
         
            +
                    sg.Text(
         
     | 
| 540 | 
         
            +
                        "Successfully Enrolled:             ",
         
     | 
| 541 | 
         
            +
                        key="se_c",
         
     | 
| 542 | 
         
            +
                        text_color="#7CFC00",
         
     | 
| 543 | 
         
            +
                    )
         
     | 
| 544 | 
         
            +
                ],
         
     | 
| 545 | 
         
            +
                [
         
     | 
| 546 | 
         
            +
                    sg.Text(
         
     | 
| 547 | 
         
            +
                        "Amount Saved: $                                         ",
         
     | 
| 548 | 
         
            +
                        key="as_c",
         
     | 
| 549 | 
         
            +
                        text_color="#00FA9A",
         
     | 
| 550 | 
         
            +
                    )
         
     | 
| 551 | 
         
            +
                ],
         
     | 
| 552 | 
         
            +
                [sg.Text("Already Enrolled:              ", key="ae_c", text_color="#00FFFF")],
         
     | 
| 553 | 
         
            +
                [sg.Text("Expired Courses:           ", key="e_c", text_color="#FF0000")],
         
     | 
| 554 | 
         
            +
                [sg.Text("Excluded Courses:          ", key="ex_c", text_color="#FF4500")],
         
     | 
| 555 | 
         
            +
            ]
         
     | 
| 556 | 
         
            +
             
     | 
| 557 | 
         
            +
            main_col = [
         
     | 
| 558 | 
         
            +
                [
         
     | 
| 559 | 
         
            +
                    sg.TabGroup(
         
     | 
| 560 | 
         
            +
                        [[sg.Tab("Main", main_tab), sg.Tab("Advanced", advanced_tab)]],
         
     | 
| 561 | 
         
            +
                        border_width=2,
         
     | 
| 562 | 
         
            +
                    )
         
     | 
| 563 | 
         
            +
                ],
         
     | 
| 564 | 
         
            +
                [
         
     | 
| 565 | 
         
            +
                    sg.Button(
         
     | 
| 566 | 
         
            +
                        key="Start",
         
     | 
| 567 | 
         
            +
                        tooltip="Once started will not stop until completed",
         
     | 
| 568 | 
         
            +
                        image_data=start,
         
     | 
| 569 | 
         
            +
                    )
         
     | 
| 570 | 
         
            +
                ],
         
     | 
| 571 | 
         
            +
            ]
         
     | 
| 572 | 
         
            +
             
     | 
| 573 | 
         
            +
            if (
         
     | 
| 574 | 
         
            +
                udemy.settings["stay_logged_in"]["auto"]
         
     | 
| 575 | 
         
            +
                or udemy.settings["stay_logged_in"]["manual"]
         
     | 
| 576 | 
         
            +
            ):
         
     | 
| 577 | 
         
            +
                logout_btn_lo = sg.Button(key="Logout", image_data=logout)
         
     | 
| 578 | 
         
            +
            else:
         
     | 
| 579 | 
         
            +
                logout_btn_lo = sg.Button(key="Logout", image_data=logout, visible=False)
         
     | 
| 580 | 
         
            +
             
     | 
| 581 | 
         
            +
            main_lo = [
         
     | 
| 582 | 
         
            +
                [
         
     | 
| 583 | 
         
            +
                    sg.Menu(
         
     | 
| 584 | 
         
            +
                        menu,
         
     | 
| 585 | 
         
            +
                        key="mn",
         
     | 
| 586 | 
         
            +
                    )
         
     | 
| 587 | 
         
            +
                ],
         
     | 
| 588 | 
         
            +
                [sg.Text(f"Logged in as: {udemy.display_name}", key="user_t"), logout_btn_lo],
         
     | 
| 589 | 
         
            +
                [
         
     | 
| 590 | 
         
            +
                    sg.pin(sg.Column(main_col, key="main_col")),
         
     | 
| 591 | 
         
            +
                    sg.pin(sg.Column(output_col, key="output_col", visible=False)),
         
     | 
| 592 | 
         
            +
                    sg.pin(sg.Column(scrape_col, key="scrape_col", visible=False)),
         
     | 
| 593 | 
         
            +
                    sg.pin(sg.Column(done_col, key="done_col", visible=False)),
         
     | 
| 594 | 
         
            +
                ],
         
     | 
| 595 | 
         
            +
                [sg.Button(key="Exit", image_data=exit_)],
         
     | 
| 596 | 
         
            +
            ]
         
     | 
| 597 | 
         
            +
             
     | 
| 598 | 
         
            +
            # ,sg.Button(key='Dummy',image_data=back)
         
     | 
| 599 | 
         
            +
             
     | 
| 600 | 
         
            +
            global main_window
         
     | 
| 601 | 
         
            +
             
     | 
| 602 | 
         
            +
            # position windows in center
         
     | 
| 603 | 
         
            +
            main_window = sg.Window(
         
     | 
| 604 | 
         
            +
                main_title,
         
     | 
| 605 | 
         
            +
                main_lo,
         
     | 
| 606 | 
         
            +
                finalize=True,
         
     | 
| 607 | 
         
            +
            )
         
     | 
| 608 | 
         
            +
            threading.Thread(target=update_enrolled_courses, daemon=True).start()
         
     | 
| 609 | 
         
            +
            while True:
         
     | 
| 610 | 
         
            +
                event, values = main_window.read()
         
     | 
| 611 | 
         
            +
                if event == "Dummy":
         
     | 
| 612 | 
         
            +
                    print(values)
         
     | 
| 613 | 
         
            +
             
     | 
| 614 | 
         
            +
                if event in (None, "Exit"):
         
     | 
| 615 | 
         
            +
                    break
         
     | 
| 616 | 
         
            +
             
     | 
| 617 | 
         
            +
                elif event == "Logout":
         
     | 
| 618 | 
         
            +
                    (
         
     | 
| 619 | 
         
            +
                        udemy.settings["stay_logged_in"]["auto"],
         
     | 
| 620 | 
         
            +
                        udemy.settings["stay_logged_in"]["manual"],
         
     | 
| 621 | 
         
            +
                    ) = (
         
     | 
| 622 | 
         
            +
                        False,
         
     | 
| 623 | 
         
            +
                        False,
         
     | 
| 624 | 
         
            +
                    )
         
     | 
| 625 | 
         
            +
                    udemy.save_settings()
         
     | 
| 626 | 
         
            +
                    break
         
     | 
| 627 | 
         
            +
             
     | 
| 628 | 
         
            +
                elif event == "Support":
         
     | 
| 629 | 
         
            +
                    web(LINKS["support"])
         
     | 
| 630 | 
         
            +
             
     | 
| 631 | 
         
            +
                elif event == "Github":
         
     | 
| 632 | 
         
            +
                    web(LINKS["github"])
         
     | 
| 633 | 
         
            +
             
     | 
| 634 | 
         
            +
                elif event == "Discord":
         
     | 
| 635 | 
         
            +
                    web(LINKS["discord"])
         
     | 
| 636 | 
         
            +
             
     | 
| 637 | 
         
            +
                elif event == "Start" and main_window["main_col"].visible:
         
     | 
| 638 | 
         
            +
                    # for key in udemy.settings["languages"]:
         
     | 
| 639 | 
         
            +
                    #     udemy.settings["languages"][key] = values[key]
         
     | 
| 640 | 
         
            +
                    # for key in udemy.settings["categories"]:
         
     | 
| 641 | 
         
            +
                    #     udemy.settings["categories"][key] = values[key]
         
     | 
| 642 | 
         
            +
                    # for key in udemy.settings["sites"]:
         
     | 
| 643 | 
         
            +
                    #     udemy.settings["sites"][key] = values[key]
         
     | 
| 644 | 
         
            +
                    for setting in ["languages", "categories", "sites"]:
         
     | 
| 645 | 
         
            +
                        for key in udemy.settings[setting]:
         
     | 
| 646 | 
         
            +
                            udemy.settings[setting][key] = values[key]
         
     | 
| 647 | 
         
            +
             
     | 
| 648 | 
         
            +
                    udemy.settings["instructor_exclude"] = str(values["instructor_exclude"]).split()
         
     | 
| 649 | 
         
            +
                    udemy.settings["title_exclude"] = list(
         
     | 
| 650 | 
         
            +
                        filter(None, values["title_exclude"].split("\n"))
         
     | 
| 651 | 
         
            +
                    )
         
     | 
| 652 | 
         
            +
                    udemy.settings["min_rating"] = float(values["min_rating"])
         
     | 
| 653 | 
         
            +
                    udemy.settings["course_update_threshold_months"] = int(
         
     | 
| 654 | 
         
            +
                        values["course_update_threshold_months"]
         
     | 
| 655 | 
         
            +
                    )
         
     | 
| 656 | 
         
            +
                    udemy.settings["save_txt"] = values["save_txt"]
         
     | 
| 657 | 
         
            +
                    udemy.settings["discounted_only"] = values["discounted_only"]
         
     | 
| 658 | 
         
            +
                    udemy.save_settings()
         
     | 
| 659 | 
         
            +
             
     | 
| 660 | 
         
            +
                    user_dumb = udemy.is_user_dumb()
         
     | 
| 661 | 
         
            +
                    if user_dumb:
         
     | 
| 662 | 
         
            +
                        sg.popup_auto_close(
         
     | 
| 663 | 
         
            +
                            "What do you even expect to happen!",
         
     | 
| 664 | 
         
            +
                            auto_close_duration=5,
         
     | 
| 665 | 
         
            +
                            no_titlebar=True,
         
     | 
| 666 | 
         
            +
                        )
         
     | 
| 667 | 
         
            +
                        continue
         
     | 
| 668 | 
         
            +
                    scraper = Scraper(udemy.sites)
         
     | 
| 669 | 
         
            +
                    udemy.window = main_window
         
     | 
| 670 | 
         
            +
                    threading.Thread(target=scrape, daemon=True).start()
         
     | 
| 671 | 
         
            +
             
     | 
| 672 | 
         
            +
                elif event == "Error":
         
     | 
| 673 | 
         
            +
                    msg = values["Error"].split("|:|")
         
     | 
| 674 | 
         
            +
                    e = msg[0]
         
     | 
| 675 | 
         
            +
                    title = msg[1]
         
     | 
| 676 | 
         
            +
                    sg.popup_scrolled(e, title=title)
         
     | 
| 677 | 
         
            +
                elif event == "Update-Menu":
         
     | 
| 678 | 
         
            +
                    menu = values["Update-Menu"]
         
     | 
| 679 | 
         
            +
                    main_window["mn"].update(menu)
         
     | 
| 680 | 
         
            +
            main_window.close()
         
     | 
    	
        images.py
    ADDED
    
    | 
         @@ -0,0 +1,9 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            auto_login = b"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"
         
     | 
| 2 | 
         
            +
            manual_login_ = b"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"
         
     | 
| 3 | 
         
            +
            login = b"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"
         
     | 
| 4 | 
         
            +
            back = b"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"
         
     | 
| 5 | 
         
            +
            start = b"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"
         
     | 
| 6 | 
         
            +
            exit_ = b"iVBORw0KGgoAAAANSUhEUgAAAC4AAAAaCAYAAADIUm6MAAAEP0lEQVRYhc2YX0xTVxzHv+f+6W0pHcgfZyiBKgZfZPAAsuALZIvp4jTAqJoQF5NFwahxPvioIfwJEZfgA+NFUf6oi5EYzFRMNMxpRkKmWRUzm3ZEtsCAkEihlN7b23vPHgqFspZ2rFg/yUnO+Z1ffvdzTm7OPbmEUopQ0Pn5ZO/jx1/7XrzYo9jthdTp/DhkYizheYls2jTJbt06zO3adZ8vKeljUlImQ6WS1eLU40kUu7rqvQ8e1ECSEjZcdi14XhSqqxuEqqrvCM97V04FiSsjIwULDQ296sREznuXXANm2zarvrl5D5OcPL0UC4grY2O57jNnfqFzc2lxM1wDkp7+l/7Chc9Zo9EBAAzgfz0Wzp//8UOVBgA6PZ3laWnpporCAYviYmdnozo+nhtftcgoNtun0u3bZwGAKJOT2a4jR0agqmy8xaKBJCVNG27cyOTkp08toaSZjAwYrl0LW0B+/hzqxASEffsAAGJPD6Tr1wEAXFER9I2NAACf3Q73qVP46O5dEK0WPpsN7tOnkdDUBL6wcE1JxeHA/MmTQTE6O5suDw6WM75Xr0r/05JXIHV1gbpcAAChshIkMdHfP3zY/xBKIba3r7d8WBSr9TNOGR3dGSnR++QJxKtXg4OSBOpyQezuhu7ECRC9HpqqKig2G7gdOwAA8sAAlDdvQtb0XLwIjyAAAPiSEuhqa/3xy5chP3vmT5Ll0OIjI/kcnZmJ/EUURdCpqdCLuncPmr17wZpMEMrLoS7mUVGEeOVK2JLU6Vzuz84G9cM9KyD+9u0nHGRZG8lbYzZDYzYHxurcHFwWy+JAhae9HYktLSA6HViTCQAg3bwJ+u5dpNLrw+vVMbGoo7x8CWVsLDCmqgpvf38sSoeFiybJ++gRPG1ty4FV9xtu926wmZmBMWEYCBYLxI6O2FiGILodVxRAFJebJC3P8Tx0x44BAKjLBcXhAABoKirAGI0xF14iOnFBAElLC24Gg3/q4EEwW7YAAKTe3sAuE56H9vjxDZEmqanjHElOnop019aUlUFTVhYUk4eG4Glrg3DgAABAdToh9fUBogjf8DC4vDzwRUXgiovhGxqKqTiTkfEHw27f/tt6C+hqakAWz2Lp1i3/awRA7OwM5GhrawGe/3+mq2AyM+1E6u//xtPaGv7A/QBJqK//kuFLS3+AICzEWyZq9HonV1AwwBCtdkGorGyNt0+06I4ePUsEwcMAgFBdXc9kZf0eb6lIsPn5P/FmcweweBwSnvcm1NWVk82b/4yvWniY7OzX+nPnviKEUGDFOc4ajY7ES5dK2NzcX+OnFxo2L+9nfVPTF8RgmFmK/fv3hKJw3jt3vhV7euogSfr3brkCkpLyt3DoULNm//7vl3Y6MBf2h5DbneSzWsvkwcEKdWoqWx0d3UldrtQNNRUEN2syvWZzcqxcYeFDrrj4PuG4kJfyfwAG+6Z6AHIPmAAAAABJRU5ErkJggg=="
         
     | 
| 7 | 
         
            +
            icon = b"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"
         
     | 
| 8 | 
         
            +
            check_mark = b"iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABhUlEQVQ4T2NkwAEsHCwciluKp8goymoyMjIygZT9////35P7T270VHfnnDx4cj82rYzogiCD+pdO2AszBJeFIMMLowucTxw4cQBZDYqBnXM7l9l7OkTiMgSb+MEdB5eXJ5VFweTgBnbO7Vpm72lPkmEwQw5uP7C8PLkcbCjYQJA3JyybiDVMiHVtQVS+I8j7YAOPPz3xl1CYYTP46LNjDNZSVmApUJhaSlswM5rbmztOXD5pH7EuganrOt3D8OvfbwZHWXu4oSBXMq4+suaKrJKsNikGdp3pZfj19xcDKxMrQ7lpCVzr43uPrjKS6l1chsG8zXji2cn/6K5DDhtkOXyGwZMNuoEgw/Y/PojhHWIMAycbbF5G1lxmWszQfaYPa5ih+wwU04yrj6y+IqskhxEpMENhmtAjAFskgiMFX6Im1psww8HJhlDC7j83iaHQKI9gqoInbJpkPZChVC0cYP6havEFM9TS0dK+b0n/PkKFBVEFLHLI464CHl/vrenNQS+pYXoB5qvqn2cyjBsAAAAASUVORK5CYII="
         
     | 
| 9 | 
         
            +
            logout = b"iVBORw0KGgoAAAANSUhEUgAAAFQAAAAVCAYAAADYb8kIAAAE6UlEQVRYhe2Ya0iUWRjHf/M6tV4qUXGbhIomzVoyzTSVxHSnsHC3oqCLyZIJ2hcRFqwPW21Bl8VbN1PaFjIqtaiItVhro0zFWGtN7YIkmpfQpWg1K9Oaxv3w7Kw644y6LQ1T+4eXl/Oe5znnf/7v8zzncFR9fX2YobfXiWvXvuH27RhqanT09jqbG31iUKt7cXP7A2/v39HpjjNv3i+o1W9NzVSDBDUYFIqLEyko+J7OTs2H5Gt38PB4TErKRubO/XXg535BDQaFQ4d+4urVeFvws1usXJlOfPxmY1P5pyM3N+d/Mf8Fzp9P5cSJXcamCFpSEktxcZLNSNk7zpz5jsrKrwAUenqcOXp0v6052T2OHUvDYFAUysrW0tXlaWagKFBUBKmpNmA3AJ6eMG2a5f6ZMyE7G86ehb17wd19aDvjegY+cXGWx3V1hZMnYenSkfF4/HgWVVVLFCoqVg6/Khti40ZYs8Zyf1ISqNWQmwuzZsGyZdbHq6uDzEx5ysst2716BZcvi/1IeABUVn6tpqlpjnWrIbBgAaxfDxMnQksLHDkiE48dCykpEBQEzn8fXXt6YMMGCAgY2mfKFDh8GI4flyjz94ddu2D/fvDykrlUKomozEwoKRnMxWCAFy+gsVGi8Plz69yfPBk8RlycCLVnD1RXy4/p6ZH26tWg10N4+PA8AKqrdQqdnZ+PSszJk6UMPHsGOTng5ARbt4qYixZBRARs3y7pB5CeLmloyccaSktlQXfvwpYtUFVlbnPxoqR9ZibU1MClS9bHjIjoT/nQUCgshOZmSEyEhARwc5Of+XbAmX0kPAA6Oyeq0es/s87ABP7+4OAAp05JhDk6wqZN4Ovbb6NWw5gxI/OxFlHNzfLu6oIHD8z73d0hNlYiytERHj6UKCoshPp62LbN3Keurl/0+noR6sAByMiA6Gi4cEFsJk0aOQ8jXr+eoLbcawFqCy56PVy5Ajod7N4tNaioCG7dguXLLfsY4eAgb1fXkXOJjgaNBnbuhAkTIDkZwsLAxUWEGQqmKQ8wfryUi9HObwpPz2aFceP+tGqk0YhIOp2kSFUVvHsnkREZCTEx0NEhNUyjgRkzZHc8eFCKvkpl3aejA/r6pE4tXAirVg2e/+VLmD4doqJAqx3cZ4zumBiJ0IYGKUmtrXDnzvDr0Wql1icnQ1OTZFBUFMyfb+5njUe/oC0OO2Jj59Pa+oVZp0oF69aBh4cIGRoqA+XnQ1ubREJkJDx9CllZ8uddXESUkBCpVYsXi09RkWWfN2+kXgUFgZ+fpOOcORLZjx5Jf3CwPPfvi1hGNDTInAEBIkJ3N1y/LmNptXDzpmxaltbT1SWbzezZsgmVl0s7PBwqKmDJEqmb9+5Z52FEcPAlVd+NG2tJTy8YWvJRIjtbdvCsLFlIYqKIahp1HysyMkIVwsLOo9E0/CcDOjnJzu3hIcchHx8p/J8Cpk69i6/vb3LbVFa2mrS00+89aGCgHIC9vKTm1NRAXp4clz5mKIqeffuC0Wqr+6/v8vJ+4Ny5LbZlZqdISPiWFSv2gekFc37+DgoKtgMqG1GzPwwQE0wFBaitjSI7+0fa270/NDe7go/PLeLjN+PnVzLws7mgILf3tbVfUlq6lvZ2bxobA+jufo8T70cAZ+fnuLu3ERLyM4GBl/HzK0GlMhPvLzg09RVrdeiVAAAAAElFTkSuQmCC"
         
     | 
    	
        requirements.txt
    ADDED
    
    | 
         @@ -0,0 +1,9 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            FreeSimpleGUI
         
     | 
| 2 | 
         
            +
            rookiepy
         
     | 
| 3 | 
         
            +
            cloudscraper
         
     | 
| 4 | 
         
            +
            bs4
         
     | 
| 5 | 
         
            +
            requests
         
     | 
| 6 | 
         
            +
            html5lib
         
     | 
| 7 | 
         
            +
            pyopenssl
         
     | 
| 8 | 
         
            +
            colorama
         
     | 
| 9 | 
         
            +
            tqdm
         
     |