├── nodejs ├── .npmignore ├── .eslintrc.json ├── FlightRadar24 │ ├── util.js │ ├── errors.js │ ├── index.js │ ├── entities │ │ ├── entity.js │ │ ├── airport.js │ │ └── flight.js │ ├── flightTrackerConfig.js │ ├── zones.js │ ├── request.js │ ├── core.js │ ├── index.d.ts │ └── api.js ├── package.json ├── LICENSE ├── README.md ├── Makefile └── tests │ └── testApi.js ├── python ├── requirements.txt ├── tests │ ├── conftest.py │ ├── package.py │ ├── util.py │ └── test_api.py ├── FlightRadar24 │ ├── entities │ │ ├── __init__.py │ │ ├── entity.py │ │ ├── airport.py │ │ └── flight.py │ ├── errors.py │ ├── __init__.py │ ├── request.py │ ├── zones.py │ ├── core.py │ └── api.py ├── .flake8 ├── pytest.ini ├── LICENSE ├── pyproject.toml ├── README.md └── Makefile ├── docs ├── assets │ ├── logo.png │ └── favicon.ico ├── projects.md ├── index.md ├── python.md └── nodejs.md ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── bug_report.md │ └── questioning.md └── workflows │ ├── node-package.yml │ ├── deploy-docs.yml │ └── python-package.yml ├── LICENSE ├── mkdocs.yml └── README.md /nodejs/.npmignore: -------------------------------------------------------------------------------- 1 | .eslintrc.json 2 | tests/ -------------------------------------------------------------------------------- /python/requirements.txt: -------------------------------------------------------------------------------- 1 | Brotli 2 | pytest 3 | requests 4 | beautifulsoup4 5 | -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeanExtreme002/FlightRadarAPI/HEAD/docs/assets/logo.png -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeanExtreme002/FlightRadarAPI/HEAD/docs/assets/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | dist 3 | *.pytest_cache 4 | *.egg-info 5 | .idea/ 6 | venv/ 7 | node_modules/ 8 | .env 9 | .cache -------------------------------------------------------------------------------- /python/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | current_dir = os.getcwd() 5 | sys.path.append(current_dir) 6 | -------------------------------------------------------------------------------- /python/FlightRadar24/entities/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .airport import Airport 4 | from .entity import Entity 5 | from .flight import Flight 6 | -------------------------------------------------------------------------------- /python/tests/package.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from FlightRadar24 import __version__ as version 4 | from FlightRadar24 import Countries, FlightRadar24API 5 | from FlightRadar24.errors import CloudflareError 6 | -------------------------------------------------------------------------------- /python/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | 3 | max-line-length = 130 4 | ignore = W503, E701, E722 5 | 6 | per-file-ignores = 7 | # __init__.py files are allowed to have unused imports and lines-too-long 8 | */__init__.py:F401 9 | 10 | # Unused imports are allowed in the tests/package.py module and they must come after setting the current working directory. 11 | tests/package.py:F401, E402 -------------------------------------------------------------------------------- /python/FlightRadar24/errors.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | class AirportNotFoundError(Exception): 4 | pass 5 | 6 | 7 | class CloudflareError(Exception): 8 | def __init__(self, message, response): 9 | self.message = message 10 | self.response = response 11 | 12 | def __str__(self): 13 | return self.message 14 | 15 | 16 | class LoginError(Exception): 17 | pass 18 | -------------------------------------------------------------------------------- /python/pytest.ini: -------------------------------------------------------------------------------- 1 | [tool:pytest] 2 | testpaths = tests 3 | python_files = test_*.py 4 | python_classes = Test* 5 | python_functions = test_* 6 | addopts = 7 | -v 8 | --tb=short 9 | --strict-markers 10 | --disable-warnings 11 | --color=yes 12 | markers = 13 | slow: marks tests as slow (deselect with '-m "not slow"') 14 | unit: marks tests as unit tests 15 | integration: marks tests as integration tests 16 | -------------------------------------------------------------------------------- /nodejs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": "google", 8 | "parserOptions": { 9 | "ecmaVersion": "latest" 10 | }, 11 | "rules": { 12 | "max-len": ["error", {"code": 130}], 13 | "quotes": ["warn", "double"], 14 | "indent": ["warn", 4], 15 | "brace-style": ["warn", "stroustrup"], 16 | "valid-jsdoc": ["warn"], 17 | "require-jsdoc": ["warn"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if the string is an integer 3 | * 4 | * @param {string} text 5 | * @return {boolean} 6 | */ 7 | function isNumeric(text) { 8 | if (text.length === 0) { 9 | return false; 10 | } 11 | 12 | for (let index = 0; index < text.length; index++) { 13 | if (!"0123456789".includes(text[index])) { 14 | return false; 15 | } 16 | } 17 | return true; 18 | } 19 | 20 | module.exports = {isNumeric}; 21 | -------------------------------------------------------------------------------- /python/FlightRadar24/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Unofficial SDK for FlightRadar24. 5 | 6 | This SDK provides flight and airport data available to the public 7 | on the FlightRadar24 website. 8 | 9 | See more information at: 10 | https://www.flightradar24.com/premium/ 11 | https://www.flightradar24.com/terms-and-conditions 12 | """ 13 | 14 | __author__ = "Jean Loui Bernard Silva de Jesus" 15 | __version__ = "1.4.0" 16 | 17 | from .api import Countries, FlightRadar24API, FlightTrackerConfig 18 | from .entities import Airport, Entity, Flight 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | - A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | - A clear and concise description of what you want to happen. 13 | - A clear and concise description of any alternative solutions or features you've considered. 14 | 15 | **Is not your feature request related to a problem? Please describe** 16 | A clear and concise description of how your feature can positively impact the project. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Set this input '....' 17 | 3. Run the '....' 18 | 4. Scroll down to '....' 19 | 5. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **System (please complete the following information):** 28 | - OS: [e.g. Windows] 29 | - Python Version [e.g. 1.10] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/errors.js: -------------------------------------------------------------------------------- 1 | class AirportNotFoundError extends Error { 2 | constructor(message) { 3 | super(message); 4 | 5 | this.name = this.constructor.name; 6 | 7 | Error.captureStackTrace(this, this.constructor); 8 | } 9 | } 10 | 11 | class CloudflareError extends Error { 12 | constructor(message, response) { 13 | super(message); 14 | 15 | this.name = this.constructor.name; 16 | this.response = response; 17 | 18 | Error.captureStackTrace(this, this.constructor); 19 | } 20 | } 21 | 22 | class LoginError extends Error { 23 | constructor(message) { 24 | super(message); 25 | 26 | this.name = this.constructor.name; 27 | 28 | Error.captureStackTrace(this, this.constructor); 29 | } 30 | } 31 | 32 | module.exports = {AirportNotFoundError, CloudflareError, LoginError}; 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/questioning.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Questioning 3 | about: Ask a question about the project 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your problem described in the documentation? If so, please describe** 11 | A clear and concise description of what is confusing in the documentation. 12 | 13 | **Describe your question** 14 | A clear and concise description of what the bug is. 15 | 16 | **Is your question reproducible? Please describe** 17 | Steps to reproduce the behavior: 18 | 19 | 1. Go to '...' 20 | 2. Set this input '....' 21 | 3. Run the '....' 22 | 4. Scroll down to '....' 23 | 5. See behavior 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **System** 29 | If applicable, please complete the following information: 30 | 31 | 1. OS: [e.g. Windows] 32 | 2. Python Version [e.g. 1.10] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /python/FlightRadar24/entities/entity.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from abc import ABC 4 | from math import acos, cos, radians, sin 5 | 6 | 7 | class Entity(ABC): 8 | """ 9 | Representation of a real entity, at some location. 10 | """ 11 | 12 | _default_text = "N/A" 13 | 14 | def __init__(self, latitude: float, longitude: float): 15 | """ 16 | Constructor of the Entity class. 17 | """ 18 | self.latitude = latitude 19 | self.longitude = longitude 20 | 21 | def get_distance_from(self, entity: "Entity") -> float: 22 | """ 23 | Return the distance from another entity (in kilometers). 24 | """ 25 | lat1, lon1 = self.latitude, self.longitude 26 | lat2, lon2 = entity.latitude, entity.longitude 27 | 28 | lat1, lon1 = radians(lat1), radians(lon1) 29 | lat2, lon2 = radians(lat2), radians(lon2) 30 | 31 | return acos(sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon2 - lon1)) * 6371 32 | -------------------------------------------------------------------------------- /.github/workflows/node-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | schedule: 12 | - cron: '0 0 */7 * *' 13 | workflow_dispatch: 14 | 15 | defaults: 16 | run: 17 | working-directory: ./nodejs 18 | 19 | jobs: 20 | build: 21 | runs-on: ubuntu-latest 22 | strategy: 23 | matrix: 24 | node-version: ['16.x', '18.x', '20.x'] 25 | steps: 26 | - uses: actions/checkout@v3 27 | - name: Set up NodeJS ${{ matrix.python-version }} 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | - name: Install dependencies 32 | run: npm ci 33 | - name: Test package 34 | run: npm test 35 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Unofficial SDK for FlightRadar24. 3 | * 4 | * This SDK provides flight and airport data available to the public 5 | * on the FlightRadar24 website. 6 | * 7 | * See more information at: 8 | * https://www.flightradar24.com/premium/ 9 | * https://www.flightradar24.com/terms-and-conditions 10 | */ 11 | 12 | const {AirportNotFoundError, CloudflareError, LoginError} = require("./errors"); 13 | const FlightRadar24API = require("./api"); 14 | const FlightTrackerConfig = require("./flightTrackerConfig"); 15 | const Airport = require("./entities/airport"); 16 | const Entity = require("./entities/entity"); 17 | const Flight = require("./entities/flight"); 18 | const {Countries} = require("./core"); 19 | 20 | const author = "Jean Loui Bernard Silva de Jesus"; 21 | const version = "1.4.0"; 22 | 23 | module.exports = { 24 | FlightRadar24API, 25 | FlightTrackerConfig, 26 | Countries, 27 | Airport, Entity, Flight, 28 | AirportNotFoundError, CloudflareError, LoginError, 29 | author, version, 30 | }; 31 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy MkDocs 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - 'mkdocs.yml' 8 | - 'docs/**' 9 | - '.github/workflows/deploy-docs.yml' 10 | permissions: 11 | contents: write 12 | jobs: 13 | deploy: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Configure Git Credentials 18 | run: | 19 | git config user.name github-actions[bot] 20 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 21 | - uses: actions/setup-python@v5 22 | with: 23 | python-version: 3.x 24 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 25 | - uses: actions/cache@v4 26 | with: 27 | key: mkdocs-material-${{ env.cache_id }} 28 | path: .cache 29 | restore-keys: | 30 | mkdocs-material- 31 | - run: pip install \ 32 | mkdocs-material \ 33 | mkdocs-git-committers-plugin-2 34 | - run: mkdocs gh-deploy --force -------------------------------------------------------------------------------- /nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flightradarapi", 3 | "version": "1.4.0", 4 | "description": "SDK for FlightRadar24", 5 | "main": "./FlightRadar24/index.js", 6 | "scripts": { 7 | "test": "mocha tests --timeout 10000", 8 | "lint": "eslint ." 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/JeanExtreme002/FlightRadarAPI.git" 13 | }, 14 | "keywords": [ 15 | "flightradar24", 16 | "api", 17 | "radar", 18 | "aviation", 19 | "flights", 20 | "airports", 21 | "airlines", 22 | "aircraft" 23 | ], 24 | "author": "Jean Loui Bernard Silva de Jesus", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/JeanExtreme002/FlightRadarAPI/issues" 28 | }, 29 | "homepage": "https://github.com/JeanExtreme002/FlightRadarAPI#readme", 30 | "dependencies": { 31 | "form-data": "^4.0.0", 32 | "jsdom": "^24.0.0", 33 | "node-fetch": "^3.3.2" 34 | }, 35 | "devDependencies": { 36 | "chai": "^4.3.10", 37 | "eslint": "^8.56.0", 38 | "eslint-config-google": "^0.14.0", 39 | "mocha": "^10.2.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jean Loui Bernard Silva de Jesus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /nodejs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jean Loui Bernard Silva de Jesus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /python/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jean Loui Bernard Silva de Jesus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python Package 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | schedule: 12 | - cron: '0 0 */7 * *' 13 | workflow_dispatch: 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | python-version: ['3.8', '3.9', '3.10', '3.11'] 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -r python/requirements.txt 31 | - name: Test with pytest 32 | run: | 33 | cd python 34 | pytest tests -vv -s 35 | - name: Install package 36 | run: | 37 | pip install FlightRadar24 38 | -------------------------------------------------------------------------------- /python/tests/util.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, List, Optional 2 | import time 3 | 4 | 5 | def raise_multiple(errors: List) -> None: 6 | """ 7 | Raise a stack of errors. 8 | """ 9 | if len(errors) == 0: return 10 | 11 | try: raise errors.pop() 12 | finally: raise_multiple(errors) 13 | 14 | 15 | def repeat_test(attempts: int, after: int, errors: Optional[List[Exception]] = None) -> Callable: 16 | """ 17 | Decorator to repeat a test N times for specific errors. 18 | 19 | :param attempts: Number of attempts for testing 20 | :param after: Time in seconds to wait for each attempt 21 | :param errors: If None, repeat test for any error 22 | """ 23 | def _repeat_test(test_function: Callable) -> Callable: 24 | def wrapper(*args, **kwargs): 25 | nonlocal attempts, errors 26 | 27 | error_list: List[Exception] = list() 28 | 29 | for attempt in range(attempts): 30 | try: 31 | return test_function(*args, **kwargs) 32 | 33 | except Exception as error: 34 | if errors is not None and error not in errors: raise error 35 | if after is not None: time.sleep(after) 36 | 37 | error_list.append(error) 38 | 39 | raise raise_multiple(error_list) 40 | return wrapper 41 | return _repeat_test 42 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/entities/entity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Representation of a real entity, at some location. 3 | */ 4 | class Entity { 5 | static __defaultText = "N/A"; 6 | 7 | /** 8 | * Constructor of Entity class. 9 | * 10 | * @param {number} latitude 11 | * @param {number} longitude 12 | */ 13 | constructor(latitude = null, longitude = null) { 14 | this.__setPosition(latitude, longitude); 15 | } 16 | 17 | __setPosition(latitude, longitude) { 18 | this.latitude = latitude; 19 | this.longitude = longitude; 20 | } 21 | 22 | __getInfo(info, replaceBy = undefined) { 23 | replaceBy = replaceBy === undefined ? this.__defaultText : replaceBy; 24 | return (info || info === 0) && (info !== this.__defaultText) ? info : replaceBy; 25 | } 26 | 27 | /** 28 | * Return the distance from another entity (in kilometers). 29 | * 30 | * @param {Entity} entity 31 | * @return {number} 32 | */ 33 | getDistanceFrom(entity) { 34 | Math.radians = (x) => x * (Math.PI / 180); 35 | 36 | const lat1 = Math.radians(this.latitude); 37 | const lon1 = Math.radians(this.longitude); 38 | 39 | const lat2 = Math.radians(entity.latitude); 40 | const lon2 = Math.radians(entity.longitude); 41 | 42 | return Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)) * 6371; 43 | } 44 | } 45 | 46 | module.exports = Entity; 47 | -------------------------------------------------------------------------------- /python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "FlightRadarAPI" 3 | dynamic = ["version"] 4 | description = "SDK for FlightRadar24" 5 | authors = [ 6 | { name = "Jean Loui Bernard Silva de Jesus", email = "jeanextreme002@gmail.com" }, 7 | ] 8 | license = "MIT" 9 | readme = "README.md" 10 | keywords = ["flightradar24", "api", "radar", "aviation", "flights", "airports", "airlines", "aircraft"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Programming Language :: Python :: 3 :: Only", 14 | "Programming Language :: Python :: 3.7", 15 | "Programming Language :: Python :: 3.8", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11" 19 | ] 20 | exclude = ["tests", ".flake8"] 21 | requires-python = ">=3.7" 22 | dependencies = [ 23 | "Brotli", 24 | "requests", 25 | ] 26 | 27 | [tool.hatch.build.targets.wheel] 28 | packages = ["FlightRadar24"] 29 | 30 | [project.optional-dependencies] 31 | tests = [ 32 | "pytest", 33 | ] 34 | 35 | [project.urls] 36 | "Homepage" = "https://github.com/JeanExtreme002/FlightRadarAPI" 37 | "Source Code" = "https://github.com/JeanExtreme002/FlightRadarAPI" 38 | "Documentation" = "https://jeanextreme002.github.io/FlightRadarAPI/" 39 | "Bug Reports" = "https://github.com/JeanExtreme002/FlightRadarAPI/issues" 40 | 41 | [tool.hatch.version] 42 | path = "FlightRadar24/__init__.py" 43 | 44 | [build-system] 45 | requires = ["hatchling"] 46 | build-backend = "hatchling.build" 47 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/flightTrackerConfig.js: -------------------------------------------------------------------------------- 1 | const {isNumeric} = require("./util"); 2 | 3 | 4 | const proxyHandler = { 5 | set: function(target, key, value) { 6 | if (!target.hasOwnProperty(key)) { 7 | throw new Error("Unknown option: '" + key + "'"); 8 | } 9 | if ((typeof value !== "number") && (!isNumeric(value))) { 10 | throw new Error("Value must be a decimal. Got '" + key + "'"); 11 | } 12 | target[key] = value.toString(); 13 | }, 14 | }; 15 | 16 | 17 | /** 18 | * Data class with settings of the Real Time Flight Tracker. 19 | */ 20 | class FlightTrackerConfig { 21 | faa = "1"; 22 | satellite = "1"; 23 | mlat = "1"; 24 | flarm = "1"; 25 | adsb = "1"; 26 | gnd = "1"; 27 | air = "1"; 28 | vehicles = "1"; 29 | estimated = "1"; 30 | maxage = "14400"; 31 | gliders = "1"; 32 | stats = "1"; 33 | limit = "5000"; 34 | 35 | /** 36 | * Constructor of FlighTrackerConfig class. 37 | * 38 | * @param {object} data 39 | */ 40 | constructor(data) { 41 | for (const key in data) { 42 | if (!Object.prototype.hasOwnProperty.call(data, key)) { // guard-for-in 43 | continue; 44 | } 45 | const value = data[key]; 46 | 47 | if (this.hasOwnProperty(key) && (typeof value === "number" || isNumeric(value))) { 48 | this[key] = value; 49 | } 50 | } 51 | return new Proxy(this, proxyHandler); 52 | } 53 | } 54 | 55 | module.exports = FlightTrackerConfig; 56 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: FlightRadarAPI 2 | repo_url: https://github.com/JeanExtreme002/FlightRadarAPI 3 | docs_dir: docs 4 | site_description: Documentation for the FlightRadarAPI 5 | site_author: JeanExtreme002 6 | edit_uri: edit/main/docs/ 7 | 8 | nav: 9 | - Home: index.md 10 | - Python: python.md 11 | - Node.js: nodejs.md 12 | - Projects Using FlightRadarAPI: projects.md 13 | 14 | theme: 15 | name: material 16 | logo: assets/logo.png 17 | favicon: assets/favicon.ico 18 | 19 | features: 20 | - navigation.instant 21 | - navigation.instant.progress 22 | - content.code.copy 23 | - content.action.edit 24 | - search.suggest 25 | - search.highlight 26 | - search.share 27 | - content.tooltips 28 | - content.tabs.link 29 | 30 | icon: 31 | repo: fontawesome/brands/github 32 | annotation: material/arrow-right-circle 33 | 34 | palette: 35 | - primary: indigo 36 | - accent: yellow 37 | 38 | extra: 39 | social: 40 | - icon: fontawesome/brands/github 41 | link: https://github.com/JeanExtreme002 42 | 43 | markdown_extensions: 44 | - admonition 45 | - pymdownx.details 46 | - attr_list 47 | - md_in_html 48 | - abbr 49 | - attr_list 50 | - pymdownx.snippets 51 | - pymdownx.emoji: 52 | emoji_index: !!python/name:material.extensions.emoji.twemoji 53 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 54 | - pymdownx.tabbed: 55 | alternate_style: true 56 | - pymdownx.superfences 57 | 58 | plugins: 59 | - git-committers: 60 | repository: JeanExtreme002/FlightRadarAPI 61 | branch: main 62 | - search -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlightRadarAPI 2 | Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js. 3 | 4 | This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact business@fr24.com. See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions). 5 | 6 | **Official FR24 API**: https://fr24api.flightradar24.com/ 7 | 8 | [![Python Package](https://github.com/JeanExtreme002/FlightRadarAPI/workflows/Python%20Package/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions) 9 | [![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/) 10 | [![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI) 11 | [![Python Version](https://img.shields.io/badge/python-3.7+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/) 12 | [![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi) 13 | [![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/) 14 | [![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/) 15 | 16 | ## Installing FlightRadarAPI 17 | **For Python with pip:** 18 | ``` 19 | pip install FlightRadarAPI 20 | ``` 21 | 22 | **For Node.js with npm:** 23 | ``` 24 | npm install flightradarapi 25 | ``` 26 | 27 | ## Documentation 28 | Explore the documentation of FlightRadarAPI package, for Python or NodeJS, through [this site](https://JeanExtreme002.github.io/FlightRadarAPI/). 29 | -------------------------------------------------------------------------------- /docs/projects.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Projects using FlightRadarAPI 3 | description: Nice projects that use FlightRadarAPI 4 | --- 5 | 6 |
7 | 8 | - [__Automatic Calibration in Crowd-sourced Network of Spectrum Sensors__](https://dl.acm.org/doi/10.1145/3626111.3628187) 9 | 10 | --- 11 | 12 | By Ali Abedi, Joshua Sanz, Anant Sahai, from University of California (UC), Berkeley 13 | 14 | 15 | - [__Airline Status Tracker for top 10 Airport Departures in North America__](https://people.ischool.berkeley.edu/~frank.song/flight.html) 16 | 17 | --- 18 | 19 | By Adeleke Coker, Frank Song, Greg Chi, from University of California (UC), Berkeley, School of Information 20 | 21 | - [__Flight Tracker with Weather__](https://magpi.raspberrypi.com/articles/flight-tracker-with-weather) 22 | 23 | --- 24 | 25 | By Adam Paulson, a Reddit user going by the name C0wsaysmoo 26 | 27 | - [__Design and implementation project of an aircraft mobile node module for the SeamSAT-LEO constellation simulator__](https://upcommons.upc.edu/bitstream/handle/2117/394691/TFG.pdf?sequence=2&isAllowed=y) 28 | 29 | --- 30 | 31 | By David Anton Dobarro, graduate in Aerospace Technologies Engineering from Universitat Politècnica de Catalunya 32 | 33 | - [__Fridge Flight Tracker__](https://blog.colinwaddell.com/flight-tracker/) 34 | 35 | --- 36 | 37 | By Colin Waddell, a Glasgow based programmer, website designer and electronics specialist 38 | 39 | - [__Plane spotting Telegram Bot__](https://github.com/ViLsonCake/avgeek-telegram-bot) 40 | 41 | --- 42 | 43 | By Timofey Dmitrenko, aviation geek and Java Spring Boot programmer 44 | 45 |
46 | 47 | [Contribute Your Own :writing_hand:](https://github.com/JeanExtreme002/FlightRadarAPI/edit/main/docs/projects.md){ .md-button .md-button--primary } 48 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # FlightRadarAPI 2 | Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js. 3 | 4 | This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact business@fr24.com. See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions). 5 | 6 | **Official FR24 API**: https://fr24api.flightradar24.com/ 7 | 8 | [![Python Package](https://github.com/JeanExtreme002/FlightRadarAPI/workflows/Python%20Package/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions) 9 | [![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/) 10 | [![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI) 11 | [![Python Version](https://img.shields.io/badge/python-3.7+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/) 12 | [![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi) 13 | [![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/) 14 | [![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/) 15 | 16 | ## Installing FlightRadarAPI: 17 | ``` 18 | $ pip install FlightRadarAPI 19 | ``` 20 | 21 | ## Basic Usage: 22 | Import the class `FlightRadar24API` and create an instance of it. 23 | ```py 24 | from FlightRadar24 import FlightRadar24API 25 | fr_api = FlightRadar24API() 26 | ``` 27 | 28 | **Getting flights list:** 29 | ```py 30 | flights = fr_api.get_flights(...) # Returns a list of Flight objects 31 | ``` 32 | **Getting airports list:** 33 | ```py 34 | airports = fr_api.get_airports(...) # Returns a list of Airport objects 35 | ``` 36 | **Getting airlines list:** 37 | ```py 38 | airlines = fr_api.get_airlines() 39 | ``` 40 | **Getting zones list:** 41 | ```py 42 | zones = fr_api.get_zones() 43 | ``` 44 | 45 | ## Documentation 46 | Explore the documentation of FlightRadarAPI package, for Python or NodeJS, through [this site](https://JeanExtreme002.github.io/FlightRadarAPI/). 47 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # FlightRadarAPI Documentation 2 | 3 | Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js. 4 | 5 | This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact [business@fr24.com](business@fr24.com). 6 | 7 | See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions). 8 | 9 | [![Python Package](https://github.com/JeanExtreme002/FlightRadarAPI/workflows/Python%20Package/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions) 10 | [![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/) 11 | [![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI) 12 | [![Python Version](https://img.shields.io/badge/python-3.7+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/) 13 | [![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi) 14 | [![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/) 15 | [![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/) 16 | 17 | !!! info 18 | This is NOT an official FlightRadar24 API. You can access their official API [here](https://fr24api.flightradar24.com/). 19 | 20 |
21 | 22 | - :octicons-file-code-16:{ .lg .middle } __100% Free and Open Source!__ 23 | 24 | --- 25 | 26 | The code is open source and available for inspection on GitHub. 27 | 28 | 29 | - :material-sticker-check-outline:{ .lg .middle } __Python and Node.js__ 30 | 31 | --- 32 | 33 | Packages are avaiable for use on both Python and Node.js 34 | 35 |
36 | 37 |
38 | 39 | 40 | 41 |
42 | 43 | ## Installation 44 | 45 | === "Python" 46 | 47 | To install FlightRadarAPI for Python using [pip](https://pypi.org/project/FlightRadarAPI/), run the following command in your terminal: 48 | 49 | ```bash 50 | pip install FlightRadarAPI 51 | ``` 52 | 53 | === "Node.js" 54 | 55 | To install FlightRadarAPI for Node.js using [npm](https://www.npmjs.com/package/flightradarapi), run the following command in your terminal: 56 | 57 | ```bash 58 | npm install flightradarapi 59 | ``` 60 | -------------------------------------------------------------------------------- /nodejs/README.md: -------------------------------------------------------------------------------- 1 | # FlightRadarAPI 2 | Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js. 3 | 4 | This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact business@fr24.com. See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions). 5 | 6 | **Official FR24 API**: https://fr24api.flightradar24.com/ 7 | 8 | [![Node.js Package](https://github.com/JeanExtreme002/FlightRadarAPI/actions/workflows/node-package.yml/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions) 9 | [![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/) 10 | [![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI) 11 | [![Python Version](https://img.shields.io/badge/python-3.7+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/) 12 | [![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi) 13 | [![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/) 14 | [![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/) 15 | 16 | ## Installing FlightRadarAPI: 17 | ``` 18 | $ npm install flightradarapi 19 | ``` 20 | 21 | ## Basic Usage: 22 | 23 | Import the class `FlightRadar24API` and create an instance of it. 24 | ```javascript 25 | const { FlightRadar24API, Countries } = require("flightradarapi"); 26 | const frApi = new FlightRadar24API(); 27 | ``` 28 | 29 | **Getting flights list:** 30 | ```javascript 31 | let flights = await frApi.getFlights(...); // Returns a list of Flight objects 32 | ``` 33 | 34 | **Getting airports list (requires country selection):** 35 | ```javascript 36 | // Get airports from specific countries 37 | let airports = await frApi.getAirports([Countries.BRAZIL, Countries.UNITED_STATES]); // Returns a list of Airport objects 38 | ``` 39 | 40 | **Getting airlines list:** 41 | ```javascript 42 | let airlines = await frApi.getAirlines(); // Returns detailed airline information with IATA/ICAO codes 43 | ``` 44 | 45 | **Getting zones list:** 46 | ```javascript 47 | let zones = await frApi.getZones(); 48 | ``` 49 | 50 | **Using Countries enum:** 51 | ```javascript 52 | // Available countries in the Countries enum 53 | const { Countries } = require("flightradarapi"); 54 | 55 | // Examples of country codes: 56 | Countries.UNITED_STATES // "united-states" 57 | Countries.BRAZIL // "brazil" 58 | Countries.GERMANY // "germany" 59 | Countries.FRANCE // "france" 60 | // ... and many more 61 | ``` 62 | 63 | ## Documentation 64 | Explore the documentation of FlightRadarAPI package, for Python or NodeJS, through [this site](https://JeanExtreme002.github.io/FlightRadarAPI/). 65 | -------------------------------------------------------------------------------- /python/FlightRadar24/request.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Dict, List, Optional, Union 4 | 5 | import brotli 6 | import json 7 | import gzip 8 | 9 | import requests 10 | import requests.structures 11 | 12 | from .errors import CloudflareError 13 | 14 | 15 | class APIRequest(object): 16 | """ 17 | Class to make requests to the FlightRadar24. 18 | """ 19 | __content_encodings = { 20 | "": lambda x: x, 21 | "br": brotli.decompress, 22 | "gzip": gzip.decompress 23 | } 24 | 25 | def __init__( 26 | self, 27 | url: str, 28 | params: Optional[Dict] = None, 29 | headers: Optional[Dict] = None, 30 | timeout: int = 30, 31 | data: Optional[Dict] = None, 32 | cookies: Optional[Dict] = None, 33 | exclude_status_codes: List[int] = list() 34 | ): 35 | """ 36 | Constructor of the APIRequest class. 37 | 38 | :param url: URL for the request 39 | :param params: params that will be inserted on the URL for the request 40 | :param headers: headers for the request 41 | :param data: data for the request. If "data" is None, request will be a GET. Otherwise, it will be a POST 42 | :param cookies: cookies for the request 43 | :param exclude_status_codes: raise for status code except those on the excluded list 44 | """ 45 | self.url = url 46 | 47 | self.request_params = { 48 | "params": params, 49 | "headers": headers, 50 | "timeout": timeout, 51 | "data": data, 52 | "cookies": cookies 53 | } 54 | 55 | request_method = requests.get if data is None else requests.post 56 | 57 | if params: url += "?" + "&".join(["{}={}".format(k, v) for k, v in params.items()]) 58 | self.__response = request_method(url, headers=headers, cookies=cookies, data=data, timeout=timeout) 59 | 60 | if self.get_status_code() == 520: 61 | raise CloudflareError( 62 | message="An unexpected error has occurred. Perhaps you are making too many calls?", 63 | response=self.__response 64 | ) 65 | 66 | if self.get_status_code() not in exclude_status_codes: 67 | self.__response.raise_for_status() 68 | 69 | def get_content(self) -> Union[Dict, bytes]: 70 | """ 71 | Return the received content from the request. 72 | """ 73 | content = self.__response.content 74 | 75 | content_encoding = self.__response.headers.get("Content-Encoding", "") 76 | content_type = self.__response.headers["Content-Type"] 77 | 78 | # Try to decode the content. 79 | try: content = self.__content_encodings[content_encoding](content) 80 | except Exception: pass 81 | 82 | # Return a dictionary if the content type is JSON. 83 | if "application/json" in content_type: 84 | return json.loads(content) 85 | 86 | return content 87 | 88 | def get_cookies(self) -> Dict: 89 | """ 90 | Return the received cookies from the request. 91 | """ 92 | return self.__response.cookies.get_dict() 93 | 94 | def get_headers(self) -> requests.structures.CaseInsensitiveDict: 95 | """ 96 | Return the headers of the response. 97 | """ 98 | return self.__response.headers 99 | 100 | def get_response_object(self) -> requests.models.Response: 101 | """ 102 | Return the received response object. 103 | """ 104 | return self.__response 105 | 106 | def get_status_code(self) -> int: 107 | """ 108 | Return the status code of the response. 109 | """ 110 | return self.__response.status_code 111 | -------------------------------------------------------------------------------- /python/tests/test_api.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from package import CloudflareError, Countries, FlightRadar24API, version 4 | from util import repeat_test 5 | 6 | print("Testing FlightRadarAPI version %s." % version) 7 | 8 | repeat_test_config = { 9 | "attempts": 2, 10 | "after": 5, 11 | "errors": [CloudflareError] 12 | } 13 | 14 | fr_api = FlightRadar24API() 15 | 16 | 17 | @repeat_test(**repeat_test_config) 18 | def test_get_airlines(expect=100, airlines=["LAN", "GLO", "DAL", "AZU", "UAE"]): 19 | results = fr_api.get_airlines() 20 | assert len(results) >= expect 21 | 22 | found = [] 23 | 24 | for airline in results: 25 | if airline["ICAO"] in airlines and airline not in found: 26 | found.append(airline) 27 | 28 | assert len(found) == len(airlines) 29 | 30 | 31 | 32 | @repeat_test(**repeat_test_config) 33 | def test_get_airport(airports=["ATL", "LAX", "DXB", "DFW"]): 34 | for airport in airports: 35 | assert fr_api.get_airport(airport).iata == airport 36 | 37 | 38 | @repeat_test(**repeat_test_config) 39 | def test_get_airport_details(airports=["ATL", "LAX", "DXB", "DFW"]): 40 | data = ["airport", "airlines", "aircraftImages"] 41 | 42 | for airport in airports: 43 | details = fr_api.get_airport_details(airport, flight_limit=1) 44 | assert all([key in details for key in data]) and details["airport"]["pluginData"]["details"] 45 | 46 | 47 | @repeat_test(**repeat_test_config) 48 | def test_get_airports(expect=1800, countries=[Countries.BRAZIL, Countries.UNITED_STATES]): 49 | results = fr_api.get_airports(countries=countries) 50 | assert len(results) >= expect 51 | 52 | 53 | @repeat_test(**repeat_test_config) 54 | def test_get_zones(expect=5): 55 | results = fr_api.get_zones() 56 | 57 | assert len(results) >= expect 58 | 59 | for zone, data in results.items(): 60 | assert all([key in data for key in ["tl_y", "tl_x", "br_y", "br_x"]]) 61 | 62 | 63 | @repeat_test(**repeat_test_config) 64 | def test_get_flights(expect=100): 65 | results = fr_api.get_flights() 66 | assert len(results) >= expect 67 | 68 | 69 | @repeat_test(**repeat_test_config) 70 | def test_get_flight_details(): 71 | data = ["airport", "airline", "aircraft", "time", "status", "trail"] 72 | 73 | flights = fr_api.get_flights() 74 | middle = len(flights) // 2 75 | 76 | flights = flights[middle - 2: middle + 2] 77 | 78 | for flight in flights: 79 | details = fr_api.get_flight_details(flight) 80 | assert all([key in details for key in data]) and details["aircraft"] 81 | 82 | 83 | @repeat_test(**repeat_test_config) 84 | def test_get_flights_by_airline(airlines=["SWA", "GLO", "AZU", "UAL", "THY"], expect=3): 85 | count = 0 86 | 87 | for airline in airlines: 88 | flights = fr_api.get_flights(airline=airline) 89 | 90 | for flight in flights: 91 | assert flight.airline_icao == airline 92 | 93 | if len(flights) > 0: count += 1 94 | 95 | assert count >= expect 96 | 97 | 98 | @repeat_test(**repeat_test_config) 99 | def test_get_flights_by_bounds(target_zones=["northamerica", "southamerica"], expect=30): 100 | zones = fr_api.get_zones() 101 | 102 | for zone in target_zones: 103 | zone = zones[zone] 104 | bounds = fr_api.get_bounds(zone) 105 | 106 | flights = fr_api.get_flights(bounds=bounds) 107 | 108 | for flight in flights: 109 | assert zone["tl_y"] >= flight.latitude >= zone["br_y"] 110 | assert zone["tl_x"] <= flight.longitude <= zone["br_x"] 111 | 112 | assert len(flights) >= expect 113 | 114 | 115 | @repeat_test(**repeat_test_config) 116 | def test_get_airline_logo(airlines=[["WN", "SWA"], ["G3", "GLO"], ["AD", "AZU"], ["AA", "AAL"], ["TK", "THY"]]): 117 | expected = len(airlines) * 0.8 118 | found = 0 119 | 120 | for airline in airlines: 121 | result = fr_api.get_airline_logo(*airline) 122 | if result and len(result[0]) > 512: found += 1 123 | 124 | assert found >= expected 125 | 126 | 127 | @repeat_test(**repeat_test_config) 128 | def test_get_country_flag(countries=["United States", "Brazil", "Egypt", "Japan", "South Korea", "Canada"]): 129 | expected = len(countries) * 0.8 130 | found = 0 131 | 132 | for country in countries: 133 | result = fr_api.get_country_flag(country) 134 | if result and len(result[0]) > 512: found += 1 135 | 136 | assert found >= expected 137 | 138 | 139 | def test_get_bounds_by_point(): 140 | expected = "52.58594974202871,52.54997688140807,13.253064418048115,13.3122478541492" 141 | actual = fr_api.get_bounds_by_point(52.567967, 13.282644, 2000) 142 | 143 | assert actual == expected 144 | -------------------------------------------------------------------------------- /python/FlightRadar24/zones.py: -------------------------------------------------------------------------------- 1 | static_zones = { 2 | "europe": { 3 | "tl_y": 72.57, 4 | "tl_x": -16.96, 5 | "br_y": 33.57, 6 | "br_x": 53.05, 7 | "subzones": { 8 | "poland": { 9 | "tl_y": 56.86, 10 | "tl_x": 11.06, 11 | "br_y": 48.22, 12 | "br_x": 28.26 13 | }, 14 | "germany": { 15 | "tl_y": 57.92, 16 | "tl_x": 1.81, 17 | "br_y": 45.81, 18 | "br_x": 16.83 19 | }, 20 | "uk": { 21 | "tl_y": 62.61, 22 | "tl_x": -13.07, 23 | "br_y": 49.71, 24 | "br_x": 3.46, 25 | "subzones": { 26 | "london": { 27 | "tl_y": 53.06, 28 | "tl_x": -2.87, 29 | "br_y": 50.07, 30 | "br_x": 3.26 31 | }, 32 | "ireland": { 33 | "tl_y": 56.22, 34 | "tl_x": -11.71, 35 | "br_y": 50.91, 36 | "br_x": -4.4 37 | } 38 | } 39 | }, 40 | "spain": { 41 | "tl_y": 44.36, 42 | "tl_x": -11.06, 43 | "br_y": 35.76, 44 | "br_x": 4.04 45 | }, 46 | "france": { 47 | "tl_y": 51.07, 48 | "tl_x": -5.18, 49 | "br_y": 42.17, 50 | "br_x": 8.9 51 | }, 52 | "ceur": { 53 | "tl_y": 51.39, 54 | "tl_x": 11.25, 55 | "br_y": 39.72, 56 | "br_x": 32.55 57 | }, 58 | "scandinavia": { 59 | "tl_y": 72.12, 60 | "tl_x": -0.73, 61 | "br_y": 53.82, 62 | "br_x": 40.67 63 | }, 64 | "italy": { 65 | "tl_y": 47.67, 66 | "tl_x": 5.26, 67 | "br_y": 36.27, 68 | "br_x": 20.64 69 | } 70 | } 71 | }, 72 | "northamerica": { 73 | "tl_y": 75, 74 | "tl_x": -180, 75 | "br_y": 3, 76 | "br_x": -52, 77 | "subzones": { 78 | "na_n": { 79 | "tl_y": 72.82, 80 | "tl_x": -177.97, 81 | "br_y": 41.92, 82 | "br_x": -52.48 83 | }, 84 | "na_c": { 85 | "tl_y": 54.66, 86 | "tl_x": -134.68, 87 | "br_y": 22.16, 88 | "br_x": -56.91, 89 | "subzones": { 90 | "na_cny": { 91 | "tl_y": 45.06, 92 | "tl_x": -83.69, 93 | "br_y": 35.96, 94 | "br_x": -64.29 95 | }, 96 | "na_cla": { 97 | "tl_y": 37.91, 98 | "tl_x": -126.12, 99 | "br_y": 30.21, 100 | "br_x": -110.02 101 | }, 102 | "na_cat": { 103 | "tl_y": 35.86, 104 | "tl_x": -92.61, 105 | "br_y": 22.56, 106 | "br_x": -71.19 107 | }, 108 | "na_cse": { 109 | "tl_y": 49.12, 110 | "tl_x": -126.15, 111 | "br_y": 42.97, 112 | "br_x": -111.92 113 | }, 114 | "na_nw": { 115 | "tl_y": 54.12, 116 | "tl_x": -134.13, 117 | "br_y": 38.32, 118 | "br_x": -96.75 119 | }, 120 | "na_ne": { 121 | "tl_y": 53.72, 122 | "tl_x": -98.76, 123 | "br_y": 38.22, 124 | "br_x": -57.36 125 | }, 126 | "na_sw": { 127 | "tl_y": 38.92, 128 | "tl_x": -133.98, 129 | "br_y": 22.62, 130 | "br_x": -96.75 131 | }, 132 | "na_se": { 133 | "tl_y": 38.52, 134 | "tl_x": -98.62, 135 | "br_y": 22.52, 136 | "br_x": -57.36 137 | }, 138 | "na_cc": { 139 | "tl_y": 45.92, 140 | "tl_x": -116.88, 141 | "br_y": 27.62, 142 | "br_x": -75.91 143 | } 144 | } 145 | }, 146 | "na_s": { 147 | "tl_y": 41.92, 148 | "tl_x": -177.83, 149 | "br_y": 3.82, 150 | "br_x": -52.48 151 | } 152 | } 153 | }, 154 | "southamerica": { 155 | "tl_y": 16, 156 | "tl_x": -96, 157 | "br_y": -57, 158 | "br_x": -31 159 | }, 160 | "oceania": { 161 | "tl_y": 19.62, 162 | "tl_x": 88.4, 163 | "br_y": -55.08, 164 | "br_x": 180 165 | }, 166 | "asia": { 167 | "tl_y": 79.98, 168 | "tl_x": 40.91, 169 | "br_y": 12.48, 170 | "br_x": 179.77, 171 | "subzones": { 172 | "japan": { 173 | "tl_y": 60.38, 174 | "tl_x": 113.5, 175 | "br_y": 22.58, 176 | "br_x": 176.47 177 | } 178 | } 179 | }, 180 | "africa": { 181 | "tl_y": 39, 182 | "tl_x": -29, 183 | "br_y": -39, 184 | "br_x": 55 185 | }, 186 | "atlantic": { 187 | "tl_y": 52.62, 188 | "tl_x": -50.9, 189 | "br_y": 15.62, 190 | "br_x": -4.75 191 | }, 192 | "maldives": { 193 | "tl_y": 10.72, 194 | "tl_x": 63.1, 195 | "br_y": -6.08, 196 | "br_x": 86.53 197 | }, 198 | "northatlantic": { 199 | "tl_y": 82.62, 200 | "tl_x": -84.53, 201 | "br_y": 59.02, 202 | "br_x": 4.45 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/zones.js: -------------------------------------------------------------------------------- 1 | staticZones = { 2 | "europe": { 3 | "tl_y": 72.57, 4 | "tl_x": -16.96, 5 | "br_y": 33.57, 6 | "br_x": 53.05, 7 | "subzones": { 8 | "poland": { 9 | "tl_y": 56.86, 10 | "tl_x": 11.06, 11 | "br_y": 48.22, 12 | "br_x": 28.26 13 | }, 14 | "germany": { 15 | "tl_y": 57.92, 16 | "tl_x": 1.81, 17 | "br_y": 45.81, 18 | "br_x": 16.83 19 | }, 20 | "uk": { 21 | "tl_y": 62.61, 22 | "tl_x": -13.07, 23 | "br_y": 49.71, 24 | "br_x": 3.46, 25 | "subzones": { 26 | "london": { 27 | "tl_y": 53.06, 28 | "tl_x": -2.87, 29 | "br_y": 50.07, 30 | "br_x": 3.26 31 | }, 32 | "ireland": { 33 | "tl_y": 56.22, 34 | "tl_x": -11.71, 35 | "br_y": 50.91, 36 | "br_x": -4.4 37 | } 38 | } 39 | }, 40 | "spain": { 41 | "tl_y": 44.36, 42 | "tl_x": -11.06, 43 | "br_y": 35.76, 44 | "br_x": 4.04 45 | }, 46 | "france": { 47 | "tl_y": 51.07, 48 | "tl_x": -5.18, 49 | "br_y": 42.17, 50 | "br_x": 8.9 51 | }, 52 | "ceur": { 53 | "tl_y": 51.39, 54 | "tl_x": 11.25, 55 | "br_y": 39.72, 56 | "br_x": 32.55 57 | }, 58 | "scandinavia": { 59 | "tl_y": 72.12, 60 | "tl_x": -0.73, 61 | "br_y": 53.82, 62 | "br_x": 40.67 63 | }, 64 | "italy": { 65 | "tl_y": 47.67, 66 | "tl_x": 5.26, 67 | "br_y": 36.27, 68 | "br_x": 20.64 69 | } 70 | } 71 | }, 72 | "northamerica": { 73 | "tl_y": 75, 74 | "tl_x": -180, 75 | "br_y": 3, 76 | "br_x": -52, 77 | "subzones": { 78 | "na_n": { 79 | "tl_y": 72.82, 80 | "tl_x": -177.97, 81 | "br_y": 41.92, 82 | "br_x": -52.48 83 | }, 84 | "na_c": { 85 | "tl_y": 54.66, 86 | "tl_x": -134.68, 87 | "br_y": 22.16, 88 | "br_x": -56.91, 89 | "subzones": { 90 | "na_cny": { 91 | "tl_y": 45.06, 92 | "tl_x": -83.69, 93 | "br_y": 35.96, 94 | "br_x": -64.29 95 | }, 96 | "na_cla": { 97 | "tl_y": 37.91, 98 | "tl_x": -126.12, 99 | "br_y": 30.21, 100 | "br_x": -110.02 101 | }, 102 | "na_cat": { 103 | "tl_y": 35.86, 104 | "tl_x": -92.61, 105 | "br_y": 22.56, 106 | "br_x": -71.19 107 | }, 108 | "na_cse": { 109 | "tl_y": 49.12, 110 | "tl_x": -126.15, 111 | "br_y": 42.97, 112 | "br_x": -111.92 113 | }, 114 | "na_nw": { 115 | "tl_y": 54.12, 116 | "tl_x": -134.13, 117 | "br_y": 38.32, 118 | "br_x": -96.75 119 | }, 120 | "na_ne": { 121 | "tl_y": 53.72, 122 | "tl_x": -98.76, 123 | "br_y": 38.22, 124 | "br_x": -57.36 125 | }, 126 | "na_sw": { 127 | "tl_y": 38.92, 128 | "tl_x": -133.98, 129 | "br_y": 22.62, 130 | "br_x": -96.75 131 | }, 132 | "na_se": { 133 | "tl_y": 38.52, 134 | "tl_x": -98.62, 135 | "br_y": 22.52, 136 | "br_x": -57.36 137 | }, 138 | "na_cc": { 139 | "tl_y": 45.92, 140 | "tl_x": -116.88, 141 | "br_y": 27.62, 142 | "br_x": -75.91 143 | } 144 | } 145 | }, 146 | "na_s": { 147 | "tl_y": 41.92, 148 | "tl_x": -177.83, 149 | "br_y": 3.82, 150 | "br_x": -52.48 151 | } 152 | } 153 | }, 154 | "southamerica": { 155 | "tl_y": 16, 156 | "tl_x": -96, 157 | "br_y": -57, 158 | "br_x": -31 159 | }, 160 | "oceania": { 161 | "tl_y": 19.62, 162 | "tl_x": 88.4, 163 | "br_y": -55.08, 164 | "br_x": 180 165 | }, 166 | "asia": { 167 | "tl_y": 79.98, 168 | "tl_x": 40.91, 169 | "br_y": 12.48, 170 | "br_x": 179.77, 171 | "subzones": { 172 | "japan": { 173 | "tl_y": 60.38, 174 | "tl_x": 113.5, 175 | "br_y": 22.58, 176 | "br_x": 176.47 177 | } 178 | } 179 | }, 180 | "africa": { 181 | "tl_y": 39, 182 | "tl_x": -29, 183 | "br_y": -39, 184 | "br_x": 55 185 | }, 186 | "atlantic": { 187 | "tl_y": 52.62, 188 | "tl_x": -50.9, 189 | "br_y": 15.62, 190 | "br_x": -4.75 191 | }, 192 | "maldives": { 193 | "tl_y": 10.72, 194 | "tl_x": 63.1, 195 | "br_y": -6.08, 196 | "br_x": 86.53 197 | }, 198 | "northatlantic": { 199 | "tl_y": 82.62, 200 | "tl_x": -84.53, 201 | "br_y": 59.02, 202 | "br_x": 4.45 203 | } 204 | }; 205 | 206 | module.exports = {staticZones}; -------------------------------------------------------------------------------- /nodejs/FlightRadar24/request.js: -------------------------------------------------------------------------------- 1 | const {CloudflareError} = require("./errors"); 2 | 3 | const FormData = require("form-data"); 4 | const fetch = (...args) => import("node-fetch").then(({default: fetch}) => fetch(...args)); 5 | 6 | 7 | /** 8 | * Class to make requests to the FlightRadar24. 9 | */ 10 | class APIRequest { 11 | /** 12 | * Constructor of the APIRequest class. 13 | * 14 | * @param {string} [url] 15 | * @param {object} [params] 16 | * @param {object} [headers] 17 | * @param {object} [data] 18 | * @param {object} [cookies] 19 | * @param {object} [excludeStatusCodes=[]] 20 | */ 21 | constructor(url, params = null, headers = null, data = null, cookies = null, excludeStatusCodes = []) { 22 | this.requestParams = { 23 | "params": params, 24 | "headers": headers, 25 | "data": data, 26 | "cookies": cookies, 27 | }; 28 | 29 | this.requestMethod = data == null ? "GET" : "POST"; 30 | this.__excludeStatusCodes = excludeStatusCodes; 31 | 32 | if (params != null && Object.keys(params).length > 0) { 33 | url += "?"; 34 | 35 | for (const key in params) { 36 | if (Object.prototype.hasOwnProperty.call(params, key)) { // guard-for-in 37 | url += key + "=" + params[key] + "&"; 38 | } 39 | } 40 | url = url.slice(0, -1); 41 | } 42 | 43 | this.url = url; 44 | 45 | this.__response = {}; 46 | this.__content = null; 47 | } 48 | 49 | /** 50 | * Send the request and receive a response. 51 | * 52 | * @return {this} 53 | */ 54 | async receive() { 55 | const settings = { 56 | method: this.requestMethod, 57 | headers: this.requestParams["headers"], 58 | cookies: this.requestParams["cookies"], 59 | }; 60 | 61 | if (settings["method"] == "POST") { 62 | const formData = new FormData(); 63 | 64 | Object.entries(this.requestParams["data"]).forEach(([key, value]) => { 65 | formData.append(key, value); 66 | }); 67 | 68 | settings["body"] = formData; 69 | } 70 | 71 | this.__response = await fetch(this.url, settings); 72 | 73 | if (this.getStatusCode() == 520) { 74 | throw new CloudflareError( 75 | "An unexpected error has occurred. Perhaps you are making too many calls?", 76 | this.__response, 77 | ); 78 | } 79 | 80 | if (!this.__excludeStatusCodes.includes(this.getStatusCode())) { 81 | if (![200, 201, 202].includes(this.getStatusCode())) { 82 | throw new Error( 83 | "Received status code '" + 84 | this.getStatusCode() + ": " + 85 | this.__response.statusText + "' for the URL " + 86 | this.url, 87 | ); 88 | } 89 | } 90 | return this; 91 | } 92 | 93 | /** 94 | * Return the received content from the request. 95 | */ 96 | async getContent() { 97 | if (this.__content !== null) { 98 | return this.__content; 99 | } 100 | 101 | let contentType = this.getHeaders()["content-type"]; 102 | contentType = contentType == null ? "" : contentType; 103 | 104 | if (contentType.includes("application/json")) { 105 | this.__content = await this.__response.json(); 106 | } 107 | else if (contentType.includes("text")) { 108 | this.__content = await this.__response.text(); 109 | } 110 | else { 111 | this.__content = await this.__response.arrayBuffer(); 112 | } 113 | return this.__content; 114 | } 115 | 116 | /** 117 | * Return the received cookies from the request. 118 | */ 119 | getCookies() { 120 | const rawCookies = this.__response.headers.raw()["set-cookie"]; 121 | const cookies = {}; 122 | 123 | if (rawCookies == null) { 124 | return {}; 125 | } 126 | 127 | rawCookies.forEach((string) => { 128 | const keyAndValue = string.split(";")[0].split("="); 129 | cookies[keyAndValue[0]] = keyAndValue[1]; 130 | }); 131 | 132 | return cookies; 133 | } 134 | 135 | /** 136 | * Return the headers of the response. 137 | */ 138 | getHeaders() { 139 | const headersAsDict = {}; 140 | 141 | this.__response.headers.forEach((value, key) => { 142 | headersAsDict[key] = value; 143 | }); 144 | return headersAsDict; 145 | } 146 | 147 | /** 148 | * Return the received response object. 149 | */ 150 | getResponseObject() { 151 | return this.__response; 152 | } 153 | 154 | /** 155 | * Return the status code of the response. 156 | */ 157 | getStatusCode() { 158 | return this.__response.status; 159 | } 160 | } 161 | 162 | module.exports = APIRequest; 163 | -------------------------------------------------------------------------------- /docs/python.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Python 3 | description: API Documentation for Python 4 | --- 5 | 6 | ## Installation 7 | 8 | To install the FlightRadarAPI for Python, use the following pip command: 9 | 10 | ```bash 11 | pip install FlightRadarAPI 12 | ``` 13 | 14 | ## Basic Usage 15 | 16 | Start by importing the `FlightRadar24API` class and creating an instance of it: 17 | 18 | ```python 19 | from FlightRadar24 import FlightRadar24API 20 | fr_api = FlightRadar24API() 21 | ``` 22 | 23 | ### Fetching Data 24 | 25 | You can fetch various types of data using the following methods: 26 | 27 | - **Flights list:** 28 | 29 | ```python 30 | flights = fr_api.get_flights(...) # Returns a list of Flight objects 31 | ``` 32 | 33 | - **Airports list:** 34 | 35 | ```python 36 | airports = fr_api.get_airports(...) # Returns a list of Airport objects 37 | ``` 38 | 39 | - **Airlines list:** 40 | 41 | ```python 42 | airlines = fr_api.get_airlines() 43 | ``` 44 | 45 | - **Zones list:** 46 | 47 | ```python 48 | zones = fr_api.get_zones() 49 | ``` 50 | 51 | ### Fetching Detailed Information 52 | 53 | Fetch more information about a specific flight or airport using the following methods: 54 | 55 | - **Flight details:** 56 | 57 | ```python 58 | flight_details = fr_api.get_flight_details(flight) 59 | flight.set_flight_details(flight_details) 60 | 61 | print("Flying to", flight.destination_airport_name) 62 | ``` 63 | 64 | - **Airport details:** 65 | 66 | ```python 67 | airport_details = fr_api.get_airport_details(icao) 68 | ``` 69 | 70 | !!! note 71 | Arrivals and departures can have a limit `flightLimit` (max value is 100) to display. When you need to reach more than 100 flights you can use additional parameter `page` to view other pages. 72 | 73 | ## Advanced Usage 74 | 75 | ### Fetching Flights Above a Specific Position 76 | 77 | Use the `get_bounds_by_point(...)` method to fetch flights above a specific position. This method takes `latitude` and `longitude` for your position and `radius` for the distance in meters from your position to designate a tracking area. 78 | 79 | ```python 80 | # Your point is 52°34'04.7"N 13°16'57.5"E from Google Maps and radius 2km 81 | bounds = fr_api.get_bounds_by_point(52.567967, 13.282644, 2000) 82 | 83 | flights = fr_api.get_flights(bounds = bounds) 84 | ``` 85 | 86 | ### Filtering Flights and Airports 87 | 88 | Use the `get_flights(...)` method to search for flights by area line, bounds (customized coordinates or obtained by the `get_zones()` method), aircraft registration or aircraft type. 89 | 90 | ```python 91 | airline_icao = "UAE" 92 | aircraft_type = "B77W" 93 | 94 | # You may also set a custom region, such as: bounds = "73,-12,-156,38" 95 | zone = fr_api.get_zones()["northamerica"] 96 | bounds = fr_api.get_bounds(zone) 97 | 98 | emirates_flights = fr_api.get_flights( 99 | aircraft_type = aircraft_type, 100 | airline = airline_icao, 101 | bounds = bounds 102 | ) 103 | ``` 104 | 105 | ### Fetching Airport by ICAO or IATA 106 | 107 | ```python 108 | lukla_airport = fr_api.get_airport(code = "VNLK", details = True) 109 | ``` 110 | 111 | ### Calculating Distance Between Flights and Airports 112 | 113 | The `Flight` and `Airport` classes inherit from `Entity`, which contains the `get_distance_from(...)` method. This method returns the distance between the self instance and another entity in kilometers. 114 | 115 | ```python 116 | airport = fr_api.get_airport("KJFK") 117 | distance = flight.get_distance_from(airport) 118 | 119 | print(f"The flight is {distance} km away from the airport.") 120 | ``` 121 | 122 | ### Downloading Flight Data :material-information-outline:{ title="This requires a premium subscription" } 123 | 124 | 125 | ```py 126 | history_data = fr_api.get_history_data(flight, file_type="csv", time=1706529600) 127 | 128 | with open("history_data.csv", "w") as file: 129 | file.write(history_data) 130 | ``` 131 | 132 | !!! warning inline end 133 | If an invalid time is provided, a blank document will be returned. 134 | 135 | | Parameter | Description | 136 | | ------------- | ------------- | 137 | | `flight_id` | The ID of the flight. This can be obtained from any other function that returns flight details. | 138 | | `file_type` | The format of the file to download. This can be either "CSV" or "KML". | 139 | | `time` | The scheduled time of departure (STD) of the flight in UTC, as a Unix timestamp. | 140 | 141 | 142 | ### Setting and Getting Real-time Flight Tracker Parameters 143 | 144 | Set it by using the `set_flight_tracker_config(...)` method. It receives a `FlightTrackerConfig` dataclass instance, but you can also use keyword arguments directly to the method. 145 | 146 | Get the current configuration with the `get_flight_tracker_config()` method, that returns a `FlightTrackerConfig` instance. Note: creating a new `FlightTrackerConfig` instance means resetting all parameters to default. 147 | 148 | ```python 149 | flight_tracker = fr_api.get_flight_tracker_config() 150 | flight_tracker.limit = 10 151 | 152 | fr_api.set_flight_tracker_config(flight_tracker, ...) 153 | 154 | flights = fr_api.get_flights(...) # Returns only 10 flights 155 | ``` 156 | -------------------------------------------------------------------------------- /docs/nodejs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Node.js 3 | description: API Documentation for Node.js 4 | --- 5 | 6 | ## Installation 7 | 8 | To install the FlightRadarAPI for Node.js, use the following npm command: 9 | 10 | ```bash 11 | npm install flightradarapi 12 | ``` 13 | 14 | ## Basic Usage 15 | 16 | Start by importing the `FlightRadar24API` class and creating an instance of it: 17 | 18 | ```javascript 19 | const { FlightRadar24API } = require("flightradarapi"); 20 | const frApi = new FlightRadar24API(); 21 | ``` 22 | 23 | ### Fetching Data 24 | 25 | You can fetch various types of data using the following methods: 26 | 27 | - **Flights list:** 28 | 29 | ```javascript 30 | let flights = await frApi.getFlights(...); // Returns a list of Flight objects 31 | ``` 32 | 33 | - **Airports list:** 34 | 35 | ```javascript 36 | let airports = await frApi.getAirports(...); // Returns a list of Airport objects 37 | ``` 38 | 39 | - **Airlines list:** 40 | 41 | ```javascript 42 | let airlines = await frApi.getAirlines(); 43 | ``` 44 | 45 | - **Zones list:** 46 | 47 | ```javascript 48 | let zones = await frApi.getZones(); 49 | ``` 50 | 51 | ### Fetching Detailed Information 52 | 53 | Fetch more information about a specific flight or airport using the following methods: 54 | 55 | - **Flight details:** 56 | 57 | ```javascript 58 | let flightDetails = await frApi.getFlightDetails(flight); 59 | flight.setFlightDetails(flightDetails); 60 | 61 | console.log("Flying to", flight.destinationAirportName); 62 | ``` 63 | 64 | - **Airport details:** 65 | 66 | ```javascript 67 | let airportDetails = await frApi.getAirportDetails(icao); 68 | ``` 69 | 70 | !!! note 71 | Arrivals and departures can have a limit `flightLimit` (max value is 100) to display. When you need to reach more than 100 flights you can use additional parameter `page` to view other pages. 72 | 73 | ## Advanced Usage 74 | 75 | ### Fetching Flights Above a Specific Position 76 | 77 | Use the `getBoundsByPoint(...)` method to fetch flights above a specific position. This method takes `latitude` and `longitude` for your position and `radius` for the distance in meters from your position to designate a tracking area. 78 | 79 | ```javascript 80 | // Your point is 52°34'04.7"N 13°16'57.5"E from Google Maps and radius 2km 81 | let bounds = frApi.getBoundsByPoint(52.567967, 13.282644, 2000); 82 | 83 | let flights = await frApi.getFlights(null, bounds); 84 | ``` 85 | 86 | ### Filtering Flights and Airports 87 | 88 | Use the `getFlights(...)` method to search for flights by area line, bounds (customized coordinates or obtained by the `getZones()` method), aircraft registration or aircraft type. 89 | 90 | ```javascript 91 | let airlineIcao = "UAE"; 92 | let aircraftType = "B77W"; 93 | 94 | // You may also set a custom region, such as: bounds = "73,-12,-156,38" 95 | let zone = (await frApi.getZones())["northamerica"]; 96 | let bounds = frApi.getBounds(zone); 97 | 98 | let emiratesFlights = await frApi.getFlights( 99 | airlineIcao, 100 | bounds, 101 | null, 102 | aircraftType, 103 | ); 104 | ``` 105 | 106 | ### Fetching Airport by ICAO or IATA 107 | 108 | ```javascript 109 | let luklaAirport = await frApi.getAirport("VNLK", true); 110 | ``` 111 | 112 | ### Calculating Distance Between Flights and Airports 113 | 114 | The `Flight` and `Airport` classes inherit from `Entity`, which contains the `getDistanceFrom(...)` method. This method returns the distance between the self instance and another entity in kilometers. 115 | 116 | ```javascript 117 | let airport = await frApi.getAirport("KJFK"); 118 | let distance = flight.getDistanceFrom(airport); 119 | 120 | console.log("The flight is", distance, "km away from the airport."); 121 | ``` 122 | 123 | ## Downloading Flight Data :material-information-outline:{ title="This requires a premium subscription" } 124 | 125 | You can download flight data in either CSV or KML format. The method `getHistoryData(...)` is used for this purpose. It takes three parameters: 126 | 127 | !!! warning inline end 128 | If an invalid time is provided, a blank document will be returned. 129 | 130 | | Parameter | Description | 131 | | ------------- | ------------- | 132 | | `flight_id` | The ID of the flight. This can be obtained from any other function that returns flight details. | 133 | | `file_type` | The format of the file to download. This can be either "CSV" or "KML". | 134 | | `time` | The scheduled time of departure (STD) of the flight in UTC, as a Unix timestamp. | 135 | 136 | Here is an example of how to use this method: 137 | 138 | ```javascript 139 | let historyData = await frApi.getHistoryData(flight, "csv", 1706529600); 140 | 141 | const buffer = Buffer.from(historyData); 142 | fs.writeFile("history_data.csv", buffer); 143 | ``` 144 | 145 | ### Setting and Getting Real-time Flight Tracker Parameters 146 | 147 | Set it by using the `setFlightTrackerConfig(...)` method. It receives a `FlightTrackerConfig` dataclass instance, but you can also use keyword arguments directly to the method. 148 | 149 | Get the current configuration with the `getFlightTrackerConfig()` method, that returns a `FlightTrackerConfig` instance. Note: creating a new `FlightTrackerConfig` instance means resetting all parameters to default. 150 | 151 | ```javascript 152 | let flightTracker = frApi.getFlightTrackerConfig(); 153 | flightTracker.limit = 10 154 | 155 | frApi.setFlightTrackerConfig(flightTracker, ...); 156 | 157 | let flights = await frApi.getFlights(...); // Returns only 10 flights 158 | ``` 159 | -------------------------------------------------------------------------------- /nodejs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for FlightRadarAPI npm package 2 | 3 | # Variables 4 | PACKAGE_NAME = flightradarapi 5 | NODE_MODULES = node_modules 6 | DIST_DIR = dist 7 | COVERAGE_DIR = coverage 8 | 9 | # Colors for output 10 | GREEN = \033[0;32m 11 | YELLOW = \033[0;33m 12 | RED = \033[0;31m 13 | NC = \033[0m # No Color 14 | 15 | # Default target 16 | .PHONY: help 17 | help: 18 | @echo "$(GREEN)FlightRadarAPI npm package Makefile$(NC)" 19 | @echo "" 20 | @echo "Available targets:" 21 | @echo " $(YELLOW)install$(NC) - Install dependencies" 22 | @echo " $(YELLOW)test$(NC) - Run tests" 23 | @echo " $(YELLOW)lint$(NC) - Run linter" 24 | @echo " $(YELLOW)lint-fix$(NC) - Run linter and fix issues" 25 | @echo " $(YELLOW)clean$(NC) - Clean build artifacts" 26 | @echo " $(YELLOW)build$(NC) - Build the package" 27 | @echo " $(YELLOW)validate$(NC) - Validate package before publishing" 28 | @echo " $(YELLOW)publish$(NC) - Publish to npm registry" 29 | @echo " $(YELLOW)publish-dry$(NC) - Dry run publish (test without uploading)" 30 | @echo " $(YELLOW)version-patch$(NC) - Bump patch version" 31 | @echo " $(YELLOW)version-minor$(NC) - Bump minor version" 32 | @echo " $(YELLOW)version-major$(NC) - Bump major version" 33 | @echo " $(YELLOW)check-deps$(NC) - Check for outdated dependencies" 34 | @echo " $(YELLOW)update-deps$(NC) - Update dependencies" 35 | @echo " $(YELLOW)security$(NC) - Run security audit" 36 | @echo " $(YELLOW)all$(NC) - Run full build pipeline (install, lint, test, validate)" 37 | 38 | # Install dependencies 39 | .PHONY: install 40 | install: 41 | @echo "$(GREEN)Installing dependencies...$(NC)" 42 | npm install 43 | @echo "$(GREEN)Dependencies installed successfully!$(NC)" 44 | 45 | # Run tests 46 | .PHONY: test 47 | test: 48 | @echo "$(GREEN)Running tests...$(NC)" 49 | npm test 50 | @echo "$(GREEN)Tests completed!$(NC)" 51 | 52 | # Run linter 53 | .PHONY: lint 54 | lint: 55 | @echo "$(GREEN)Running linter...$(NC)" 56 | npm run lint 57 | @echo "$(GREEN)Linting completed!$(NC)" 58 | 59 | # Run linter with auto-fix 60 | .PHONY: lint-fix 61 | lint-fix: 62 | @echo "$(GREEN)Running linter with auto-fix...$(NC)" 63 | npx eslint . --fix 64 | @echo "$(GREEN)Linting and fixing completed!$(NC)" 65 | 66 | # Clean build artifacts 67 | .PHONY: clean 68 | clean: 69 | @echo "$(GREEN)Cleaning build artifacts...$(NC)" 70 | rm -rf $(NODE_MODULES) 71 | rm -rf $(DIST_DIR) 72 | rm -rf $(COVERAGE_DIR) 73 | rm -f package-lock.json 74 | rm -f *.tgz 75 | @echo "$(GREEN)Cleanup completed!$(NC)" 76 | 77 | # Build package (prepare for publishing) 78 | .PHONY: build 79 | build: install lint test 80 | @echo "$(GREEN)Building package...$(NC)" 81 | npm pack 82 | @echo "$(GREEN)Package built successfully!$(NC)" 83 | 84 | # Validate package before publishing 85 | .PHONY: validate 86 | validate: 87 | @echo "$(GREEN)Validating package...$(NC)" 88 | npm pack --dry-run 89 | @echo "$(GREEN)Package validation completed!$(NC)" 90 | 91 | # Publish to npm registry 92 | .PHONY: publish 93 | publish: validate 94 | @echo "$(YELLOW)Are you sure you want to publish to npm? [y/N]$(NC)" && read ans && [ $${ans:-N} = y ] 95 | @echo "$(GREEN)Publishing to npm...$(NC)" 96 | npm publish 97 | @echo "$(GREEN)Package published successfully!$(NC)" 98 | 99 | # Dry run publish (test without uploading) 100 | .PHONY: publish-dry 101 | publish-dry: 102 | @echo "$(GREEN)Running publish dry run...$(NC)" 103 | npm publish --dry-run 104 | @echo "$(GREEN)Publish dry run completed!$(NC)" 105 | 106 | # Version bumping 107 | .PHONY: version-patch 108 | version-patch: 109 | @echo "$(GREEN)Bumping patch version...$(NC)" 110 | npm version patch 111 | @echo "$(GREEN)Patch version bumped!$(NC)" 112 | 113 | .PHONY: version-minor 114 | version-minor: 115 | @echo "$(GREEN)Bumping minor version...$(NC)" 116 | npm version minor 117 | @echo "$(GREEN)Minor version bumped!$(NC)" 118 | 119 | .PHONY: version-major 120 | version-major: 121 | @echo "$(GREEN)Bumping major version...$(NC)" 122 | npm version major 123 | @echo "$(GREEN)Major version bumped!$(NC)" 124 | 125 | # Check for outdated dependencies 126 | .PHONY: check-deps 127 | check-deps: 128 | @echo "$(GREEN)Checking for outdated dependencies...$(NC)" 129 | npm outdated 130 | 131 | # Update dependencies 132 | .PHONY: update-deps 133 | update-deps: 134 | @echo "$(GREEN)Updating dependencies...$(NC)" 135 | npm update 136 | @echo "$(GREEN)Dependencies updated!$(NC)" 137 | 138 | # Security audit 139 | .PHONY: security 140 | security: 141 | @echo "$(GREEN)Running security audit...$(NC)" 142 | npm audit 143 | @echo "$(GREEN)Security audit completed!$(NC)" 144 | 145 | # Fix security vulnerabilities 146 | .PHONY: security-fix 147 | security-fix: 148 | @echo "$(GREEN)Fixing security vulnerabilities...$(NC)" 149 | npm audit fix 150 | @echo "$(GREEN)Security vulnerabilities fixed!$(NC)" 151 | 152 | # Full build pipeline 153 | .PHONY: all 154 | all: install lint test validate 155 | @echo "$(GREEN)Full build pipeline completed successfully!$(NC)" 156 | 157 | # Development workflow targets 158 | .PHONY: dev-setup 159 | dev-setup: install 160 | @echo "$(GREEN)Development environment setup completed!$(NC)" 161 | 162 | .PHONY: pre-commit 163 | pre-commit: lint test 164 | @echo "$(GREEN)Pre-commit checks passed!$(NC)" 165 | 166 | .PHONY: pre-publish 167 | pre-publish: all security 168 | @echo "$(GREEN)Pre-publish checks completed!$(NC)" 169 | 170 | # CI/CD targets 171 | .PHONY: ci 172 | ci: install lint test validate 173 | @echo "$(GREEN)CI pipeline completed!$(NC)" 174 | 175 | # Show package info 176 | .PHONY: info 177 | info: 178 | @echo "$(GREEN)Package Information:$(NC)" 179 | @npm list --depth=0 180 | @echo "" 181 | @echo "$(GREEN)Package Size:$(NC)" 182 | @npm pack --dry-run | grep "npm notice package size" 183 | 184 | # Login to npm (for CI/CD) 185 | .PHONY: npm-login 186 | npm-login: 187 | @echo "$(GREEN)Logging in to npm...$(NC)" 188 | npm login 189 | 190 | # Logout from npm 191 | .PHONY: npm-logout 192 | npm-logout: 193 | @echo "$(GREEN)Logging out from npm...$(NC)" 194 | npm logout 195 | 196 | # Quick release workflow 197 | .PHONY: release-patch 198 | release-patch: pre-publish version-patch publish 199 | @echo "$(GREEN)Patch release completed!$(NC)" 200 | 201 | .PHONY: release-minor 202 | release-minor: pre-publish version-minor publish 203 | @echo "$(GREEN)Minor release completed!$(NC)" 204 | 205 | .PHONY: release-major 206 | release-major: pre-publish version-major publish 207 | @echo "$(GREEN)Major release completed!$(NC)" 208 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/entities/airport.js: -------------------------------------------------------------------------------- 1 | const Entity = require("./entity"); 2 | 3 | 4 | /** 5 | * Airport representation. 6 | */ 7 | class Airport extends Entity { 8 | /** 9 | * Constructor of Airport class. 10 | * 11 | * The parameters below are optional. You can just create an Airport instance with no information 12 | * and use the setAirportDetails(...) method for having an instance with detailed information. 13 | * 14 | * @param {object} [basicInfo] - Basic information about the airport received from FlightRadar24 15 | * @param {object} [info] - Dictionary with more information about the airport received from FlightRadar24 16 | */ 17 | constructor(basicInfo = {}, info = {}) { 18 | super(); 19 | 20 | if (basicInfo && Object.keys(basicInfo).length > 0) { 21 | this.__setPosition(basicInfo["lat"], basicInfo["lon"]); 22 | this.__initializeWithBasicInfo(basicInfo); 23 | } 24 | 25 | if (info && Object.keys(info).length > 0) { 26 | this.__setPosition(info["position"]["latitude"], info["position"]["longitude"]); 27 | this.__initializeWithInfo(info); 28 | } 29 | } 30 | 31 | /** 32 | * Initialize instance with basic information about the airport. 33 | * 34 | * @param {object} basicInfo 35 | */ 36 | __initializeWithBasicInfo(basicInfo) { 37 | this.altitude = basicInfo["alt"]; 38 | 39 | this.name = basicInfo["name"]; 40 | this.icao = basicInfo["icao"]; 41 | this.iata = basicInfo["iata"]; 42 | 43 | this.country = basicInfo["country"]; 44 | } 45 | 46 | /** 47 | * Initialize instance with extra information about the airport. 48 | * 49 | * @param {object} info 50 | */ 51 | __initializeWithInfo(info) { 52 | this.altitude = info["position"]["altitude"]; 53 | 54 | this.name = info["name"]; 55 | this.icao = info["code"]["icao"]; 56 | this.iata = info["code"]["iata"]; 57 | 58 | // Location information. 59 | const position = info["position"]; 60 | 61 | this.country = position["country"]["name"]; 62 | this.countryCode = this.__getInfo(position["country"]?.["code"]); 63 | this.city = this.__getInfo(position["region"]?.["city"]); 64 | 65 | // Timezone information. 66 | const timezone = info["timezone"]; 67 | 68 | this.timezoneName = this.__getInfo(timezone?.["name"]); 69 | this.timezoneOffset = this.__getInfo(timezone?.["offset"]); 70 | this.timezoneOffsetHours = this.__getInfo(timezone?.["offsetHours"]); 71 | this.timezoneAbbr = this.__getInfo(timezone?.["abbr"]); 72 | this.timezoneAbbrName = this.__getInfo(timezone?.["abbrName"]); 73 | 74 | // Other information. 75 | this.visible = this.__getInfo(info["visible"]); 76 | this.website = this.__getInfo(info["website"]); 77 | } 78 | 79 | /** 80 | * Set airport details to the instance. Use FlightRadar24API.getAirportDetails(...) method to get it. 81 | * 82 | * @param {object} airportDetails 83 | */ 84 | setAirportDetails(airportDetails) { 85 | // Get airport data. 86 | const airport = airportDetails?.["airport"]?.["pluginData"]; 87 | 88 | // Get information about the airport. 89 | const details = airport?.["details"]; 90 | 91 | // Get location information. 92 | const position = details?.["position"]; 93 | const code = details?.["code"]; 94 | const country = position?.["country"]; 95 | const region = position?.["region"]; 96 | 97 | // Get reviews of the airport. 98 | const flightDiary = airport?.["flightdiary"]; 99 | const ratings = flightDiary?.["ratings"]; 100 | 101 | // Get schedule information. 102 | const schedule = airport?.["schedule"]; 103 | 104 | // Get timezone information. 105 | const timezone = details?.["timezone"]; 106 | 107 | // Get aircraft count. 108 | const aircraftCount = airport?.["aircraftCount"]; 109 | const aircraftOnGround = aircraftCount?.["onGround"]; 110 | 111 | // Get URLs for more information about the airport. 112 | const urls = details?.["url"]; 113 | 114 | // Basic airport information. 115 | this.name = this.__getInfo(details?.["name"]); 116 | this.iata = this.__getInfo(code?.["iata"]); 117 | this.icao = this.__getInfo(code?.["icao"]); 118 | this.altitude = this.__getInfo(position?.["elevation"]); 119 | this.latitude = this.__getInfo(position?.["latitude"]); 120 | this.longitude = this.__getInfo(position?.["longitude"]); 121 | 122 | // Airport location. 123 | this.country = this.__getInfo(country?.["name"]); 124 | this.country_code = this.__getInfo(country?.["code"]); 125 | this.country_id = this.__getInfo(country?.["id"]); 126 | this.city = this.__getInfo(region?.["city"]); 127 | 128 | // Airport timezone. 129 | this.timezoneAbbr = this.__getInfo(timezone?.["abbr"]); 130 | this.timezoneAbbrName = this.__getInfo(timezone?.["abbrName"]); 131 | this.timezoneName = this.__getInfo(timezone?.["name"]); 132 | this.timezoneOffset = this.__getInfo(timezone?.["offset"]); 133 | 134 | if (typeof this.timezoneOffset === "number") { 135 | this.timezoneOffsetHours = Math.trunc(this.timezoneOffset / 60 / 60); 136 | this.timezoneOffsetHours = this.timezoneOffsetHours + ":00"; 137 | } 138 | else { 139 | this.timezoneOffsetHours = this.__getInfo(None); 140 | } 141 | 142 | // Airport reviews. 143 | this.reviewsUrl = flightDiary?.["url"]; 144 | 145 | if (this.reviewsUrl && typeof this.reviewsUrl === "string") { 146 | this.reviewsUrl = "https://www.flightradar24.com" + this.reviewsUrl; 147 | } 148 | else { 149 | this.reviewsUrl = this.__getInfo(this.reviewsUrl); 150 | } 151 | 152 | this.reviews = this.__getInfo(flightDiary?.["reviews"]); 153 | this.evaluation = this.__getInfo(flightDiary?.["evaluation"]); 154 | 155 | this.averageRating = this.__getInfo(ratings?.["avg"]); 156 | this.totalRating = this.__getInfo(ratings?.["total"]); 157 | 158 | // Weather information. 159 | this.weather = this.__getInfo(airport?.["weather"], {}); 160 | 161 | // Runway information. 162 | this.runways = this.__getInfo(airport?.["runways"], []); 163 | 164 | // Aircraft count information. 165 | this.aircraftOnGround = this.__getInfo(aircraftOnGround?.["total"]); 166 | this.aircraftVisibleOnGround = this.__getInfo(aircraftOnGround?.["visible"]); 167 | 168 | // Schedule information. 169 | this.arrivals = this.__getInfo(schedule?.["arrivals"], {}); 170 | this.departures = this.__getInfo(schedule?.["departures"], {}); 171 | 172 | // Link for the homepage and more information 173 | this.website = this.__getInfo(urls?.["homepage"]); 174 | this.wikipedia = this.__getInfo(urls?.["wikipedia"]); 175 | 176 | // Other information. 177 | this.visible = this.__getInfo(details?.["visible"]); 178 | this.images = this.__getInfo(details?.["airportImages"], {}); 179 | } 180 | } 181 | 182 | module.exports = Airport; 183 | -------------------------------------------------------------------------------- /python/FlightRadar24/entities/airport.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Any, Dict, Optional 4 | from .entity import Entity 5 | 6 | 7 | class Airport(Entity): 8 | """ 9 | Airport representation. 10 | """ 11 | def __init__(self, basic_info: Dict = dict(), info: Dict = dict()): 12 | """ 13 | Constructor of the Airport class. 14 | 15 | The parameters below are optional. You can just create an Airport instance with no information 16 | and use the set_airport_details(...) method for having an instance with detailed information. 17 | 18 | :param basic_info: Basic information about the airport received from FlightRadar24 19 | :param info: Dictionary with more information about the airport received from FlightRadar24 20 | """ 21 | if basic_info: self.__initialize_with_basic_info(basic_info) 22 | if info: self.__initialize_with_info(info) 23 | 24 | def __repr__(self) -> str: 25 | template = "<({}) {} - Altitude: {} - Latitude: {} - Longitude: {}>" 26 | return template.format(self.icao, self.name, self.altitude, self.latitude, self.longitude) 27 | 28 | def __str__(self) -> str: 29 | return self.__repr__() 30 | 31 | def __get_info(self, info: Any, default: Optional[Any] = None) -> Any: 32 | default = default if default is not None else self._default_text 33 | return info if info is not None and info != self._default_text else default 34 | 35 | def __initialize_with_basic_info(self, basic_info: Dict): 36 | """ 37 | Initialize instance with basic information about the airport. 38 | """ 39 | super().__init__( 40 | latitude=basic_info["lat"], 41 | longitude=basic_info["lon"] 42 | ) 43 | self.altitude = basic_info["alt"] 44 | 45 | self.name = basic_info["name"] 46 | self.icao = basic_info["icao"] 47 | self.iata = basic_info["iata"] 48 | 49 | self.country = basic_info["country"] 50 | 51 | def __initialize_with_info(self, info: Dict): 52 | """ 53 | Initialize instance with extra information about the airport. 54 | """ 55 | super().__init__( 56 | latitude=info["position"]["latitude"], 57 | longitude=info["position"]["longitude"] 58 | ) 59 | self.altitude = info["position"]["altitude"] 60 | 61 | self.name = info["name"] 62 | self.icao = info["code"]["icao"] 63 | self.iata = info["code"]["iata"] 64 | 65 | # Location information. 66 | position = info["position"] 67 | 68 | self.country = position["country"]["name"] 69 | self.country_code = self.__get_info(position.get("country", dict()).get("code")) 70 | self.city = self.__get_info(position.get("region", dict())).get("city") 71 | 72 | # Timezone information. 73 | timezone = info.get("timezone", dict()) 74 | 75 | self.timezone_name = self.__get_info(timezone.get("name")) 76 | self.timezone_offset = self.__get_info(timezone.get("offset")) 77 | self.timezone_offset_hours = self.__get_info(timezone.get("offsetHours")) 78 | self.timezone_abbr = self.__get_info(timezone.get("abbr")) 79 | self.timezone_abbr_name = self.__get_info(timezone.get("abbrName")) 80 | 81 | # Other information. 82 | self.visible = self.__get_info(info.get("visible")) 83 | self.website = self.__get_info(info.get("website")) 84 | 85 | def set_airport_details(self, airport_details: Dict) -> None: 86 | """ 87 | Set airport details to the instance. Use FlightRadar24API.get_airport_details(...) method to get it. 88 | """ 89 | # Get airport data. 90 | airport = self.__get_info(airport_details.get("airport"), dict()) 91 | airport = self.__get_info(airport.get("pluginData"), dict()) 92 | 93 | # Get information about the airport. 94 | details = self.__get_info(airport.get("details"), dict()) 95 | 96 | # Get location information. 97 | position = self.__get_info(details.get("position"), dict()) 98 | code = self.__get_info(details.get("code"), dict()) 99 | country = self.__get_info(position.get("country"), dict()) 100 | region = self.__get_info(position.get("region"), dict()) 101 | 102 | # Get reviews of the airport. 103 | flight_diary = self.__get_info(airport.get("flightdiary"), dict()) 104 | ratings = self.__get_info(flight_diary.get("ratings"), dict()) 105 | 106 | # Get schedule information. 107 | schedule = self.__get_info(airport.get("schedule"), dict()) 108 | 109 | # Get timezone information. 110 | timezone = self.__get_info(details.get("timezone"), dict()) 111 | 112 | # Get aircraft count. 113 | aircraft_count = self.__get_info(airport.get("aircraftCount"), dict()) 114 | aircraft_on_ground = self.__get_info(aircraft_count.get("onGround"), dict()) 115 | 116 | # Get URLs for more information about the airport. 117 | urls = self.__get_info(details.get("url"), dict()) 118 | 119 | # Basic airport information. 120 | self.name = self.__get_info(details.get("name")) 121 | self.iata = self.__get_info(code.get("iata")) 122 | self.icao = self.__get_info(code.get("icao")) 123 | self.altitude = self.__get_info(position.get("elevation")) 124 | self.latitude = self.__get_info(position.get("latitude")) 125 | self.longitude = self.__get_info(position.get("longitude")) 126 | 127 | # Airport location. 128 | self.country = self.__get_info(country.get("name")) 129 | self.country_code = self.__get_info(country.get("code")) 130 | self.country_id = self.__get_info(country.get("id")) 131 | self.city = self.__get_info(region.get("city")) 132 | 133 | # Airport timezone. 134 | self.timezone_abbr = self.__get_info(timezone.get("abbr")) 135 | self.timezone_abbr_name = self.__get_info(timezone.get("abbrName")) 136 | self.timezone_name = self.__get_info(timezone.get("name")) 137 | self.timezone_offset = self.__get_info(timezone.get("offset")) 138 | 139 | if isinstance(self.timezone_offset, int): 140 | self.timezone_offset_hours = int(self.timezone_offset / 60 / 60) 141 | self.timezone_offset_hours = f"{self.timezone_offset_hours}:00" 142 | else: self.timezone_offset_hours = self.__get_info(None) 143 | 144 | # Airport reviews. 145 | self.reviews_url = flight_diary.get("url") 146 | 147 | if self.reviews_url and isinstance(self.reviews_url, str): 148 | self.reviews_url = "https://www.flightradar24.com" + self.reviews_url 149 | else: 150 | self.reviews_url = self.__get_info(self.reviews_url) 151 | 152 | self.reviews = self.__get_info(flight_diary.get("reviews")) 153 | self.evaluation = self.__get_info(flight_diary.get("evaluation")) 154 | 155 | self.average_rating = self.__get_info(ratings.get("avg")) 156 | self.total_rating = self.__get_info(ratings.get("total")) 157 | 158 | # Weather information. 159 | self.weather = self.__get_info(airport.get("weather"), dict()) 160 | 161 | # Runway information. 162 | self.runways = airport.get("runways", list()) 163 | 164 | # Aircraft count information. 165 | self.aircraft_on_ground = self.__get_info(aircraft_on_ground.get("total")) 166 | self.aircraft_visible_on_ground = self.__get_info(aircraft_on_ground.get("visible")) 167 | 168 | # Schedule information. 169 | self.arrivals = self.__get_info(schedule.get("arrivals"), dict()) 170 | self.departures = self.__get_info(schedule.get("departures"), dict()) 171 | 172 | # Link for the homepage and more information 173 | self.website = self.__get_info(urls.get("homepage")) 174 | self.wikipedia = self.__get_info(urls.get("wikipedia")) 175 | 176 | # Other information. 177 | self.visible = self.__get_info(details.get("visible")) 178 | self.images = self.__get_info(details.get("airportImages"), dict()) 179 | -------------------------------------------------------------------------------- /nodejs/tests/testApi.js: -------------------------------------------------------------------------------- 1 | const {FlightRadar24API, Countries, version} = require(".."); 2 | const expect = require("chai").expect; 3 | 4 | 5 | describe("Testing FlightRadarAPI version " + version, function() { 6 | const frApi = new FlightRadar24API(); 7 | 8 | describe("Getting Airlines", function() { 9 | const expected = 100; 10 | const airlines = ["LAN", "GLO", "DAL", "AZU", "UAE"]; 11 | 12 | it("Expected at least " + expected + " airlines.", async function() { 13 | const results = await frApi.getAirlines(); 14 | expect(results.length).to.be.above(expected - 1); 15 | 16 | const found = []; 17 | 18 | for (const airline of results) { 19 | if (airlines.includes(airline.ICAO) && !found.includes(airline)) { 20 | found.push(airline); 21 | } 22 | } 23 | 24 | expect(found.length).to.be.equal(airlines.length); 25 | }); 26 | }); 27 | 28 | describe("Getting Airport By IATA", function() { 29 | const expected = ["ATL", "LAX", "DXB", "DFW"]; 30 | 31 | it("Expected finding the following airports: " + expected.join(", ") + ".", async function() { 32 | for (const iata of expected) { 33 | const airport = await frApi.getAirport(iata); 34 | expect(airport.iata).to.be.equal(iata); 35 | } 36 | }); 37 | }); 38 | 39 | describe("Getting Airport Details", function() { 40 | const expected = ["ATL", "LAX", "DXB", "DFW"]; 41 | const targetKeys = ["airport", "airlines", "aircraftImages"]; 42 | 43 | it("Expected getting details of the following airports: " + expected.join(", ") + ".", async function() { 44 | for (const iata of expected) { 45 | const details = await frApi.getAirportDetails(iata, 1); 46 | expect(details).to.include.all.keys(targetKeys); 47 | expect(details["airport"]["pluginData"]).to.include.all.keys("details"); 48 | } 49 | }); 50 | }); 51 | 52 | describe("Getting Airports", function() { 53 | const expected = 1800; 54 | const countries = [Countries.BRAZIL, Countries.UNITED_STATES]; 55 | 56 | it("Expected at least " + expected + " airports.", async function() { 57 | const results = await frApi.getAirports(countries); 58 | expect(results.length).to.be.above(expected - 1); 59 | }); 60 | }); 61 | 62 | describe("Getting Zones", function() { 63 | const expected = 5; 64 | const targetKeys = ["tl_y", "tl_x", "br_y", "br_x"]; 65 | 66 | it("Expected at least " + expected + " zones.", async function() { 67 | const results = await frApi.getZones(); 68 | expect(Object.entries(results).length).to.be.above(expected - 1); 69 | 70 | for (const key in results) { 71 | if (Object.prototype.hasOwnProperty.call(results, key)) { // guard-for-in 72 | const zone = results[key]; 73 | expect(zone).to.include.all.keys(targetKeys); 74 | } 75 | } 76 | }); 77 | }); 78 | 79 | describe("Getting Flights", function() { 80 | const expected = 100; 81 | 82 | it("Expected at least " + expected + " flights.", async function() { 83 | const results = await frApi.getFlights(); 84 | expect(results.length).to.be.above(expected - 1); 85 | }); 86 | }); 87 | 88 | describe("Getting Flight Details", function() { 89 | const targetKeys = ["airport", "airline", "aircraft", "time", "status", "trail"]; 90 | 91 | it("Expected getting the following information: " + targetKeys.join(", ") + ".", async function() { 92 | const flights = await frApi.getFlights(); 93 | const middle = Math.trunc(flights.length / 2); 94 | 95 | const someFlights = flights.slice(middle - 2, middle + 2); 96 | 97 | for (const flight of someFlights) { 98 | const details = await frApi.getFlightDetails(flight); 99 | expect(details).to.include.all.keys(targetKeys); 100 | } 101 | }); 102 | }); 103 | 104 | describe("Getting Flights by Airline", function() { 105 | const expected = 3; 106 | const targetAirlines = ["SWA", "GLO", "AZU", "UAL", "THY"]; 107 | 108 | let message = "Expected getting flights from at least " + expected; 109 | message += " of the following airlines: " + targetAirlines.join(", ") + "."; 110 | 111 | it(message, async function() { 112 | let count = 0; 113 | 114 | for (const airline of targetAirlines) { 115 | const flights = await frApi.getFlights(airline); 116 | 117 | for (const flight of flights) { 118 | expect(flight.airlineIcao).to.be.equal(airline); 119 | } 120 | 121 | count = flights.length > 0 ? count + 1 : count; 122 | } 123 | expect(count).to.be.above(expected - 1); 124 | }); 125 | }); 126 | 127 | describe("Getting Flights by Bounds", function() { 128 | const expected = 30; 129 | const targetZones = ["northamerica", "southamerica"]; 130 | 131 | it("Expected at least " + expected + " flights at: " + targetZones.join(", ") + ".", async function() { 132 | const zones = await frApi.getZones(); 133 | 134 | for (const zoneName of targetZones) { 135 | const zone = zones[zoneName]; 136 | 137 | const bounds = frApi.getBounds(zone); 138 | const flights = await frApi.getFlights(null, bounds); 139 | 140 | for (const flight of flights) { 141 | expect(flight.latitude).to.be.below(zone["tl_y"]); 142 | expect(flight.latitude).to.be.above(zone["br_y"]); 143 | 144 | expect(flight.longitude).to.be.below(zone["br_x"]); 145 | expect(flight.latitude).to.be.above(zone["tl_x"]); 146 | } 147 | expect(flights.length).to.be.above(expected - 1); 148 | } 149 | }); 150 | }); 151 | 152 | describe("Getting Airline Logo", function() { 153 | const targetAirlines = [["WN", "SWA"], ["G3", "GLO"], ["AD", "AZU"], ["AA", "AAL"], ["TK", "THY"]]; 154 | const expected = targetAirlines.length * 0.8; 155 | 156 | const icao = []; 157 | 158 | for (const airline of targetAirlines) { 159 | icao.push(airline[1]); 160 | } 161 | 162 | let message = "Expected getting logos from at least " + Math.trunc(expected); 163 | message += " of the following airlines: " + icao.join(", ") + "."; 164 | 165 | it(message, async function() { 166 | let found = 0; 167 | 168 | for (const airline of targetAirlines) { 169 | const result = await frApi.getAirlineLogo(airline[0], airline[1]); 170 | found = result != null && result[0].byteLength > 512 ? found + 1 : found; 171 | } 172 | expect(found).to.be.above(expected - 1); 173 | }); 174 | }); 175 | 176 | describe("Getting Country Flag", function() { 177 | const targetCountries = ["United States", "Brazil", "Egypt", "Japan", "South Korea", "Canada"]; 178 | const expected = targetCountries.length * 0.8; 179 | 180 | let message = "Expected getting flags from at least " + Math.trunc(expected); 181 | message += " of the following countries: " + targetCountries.join(", ") + "."; 182 | 183 | it(message, async function() { 184 | let found = 0; 185 | 186 | for (const country of targetCountries) { 187 | const result = await frApi.getCountryFlag(country); 188 | found = result != null && result[0].byteLength > 512 ? found + 1 : found; 189 | } 190 | expect(found).to.be.above(expected - 1); 191 | }); 192 | }); 193 | 194 | describe("Getting Bounds by Point", function() { 195 | const expected = "52.58594974202871,52.54997688140807,13.253064418048115,13.3122478541492"; 196 | 197 | it("Formula for calculating bounds is correct.", async function() { 198 | const bounds = frApi.getBoundsByPoint(52.567967, 13.282644, 2000); 199 | expect(bounds).to.be.equal(expected); 200 | }); 201 | }); 202 | }); 203 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/entities/flight.js: -------------------------------------------------------------------------------- 1 | const Entity = require("./entity"); 2 | 3 | /** 4 | * Flight representation. 5 | */ 6 | class Flight extends Entity { 7 | /** 8 | * Constructor of Flight class. 9 | * 10 | * @param {*} flightId - The flight ID specifically used by FlightRadar24 11 | * @param {*} info - Dictionary with received data from FlightRadar24 12 | */ 13 | constructor(flightId, info) { 14 | super(); 15 | 16 | this.__setPosition(this.__getInfo(info[1]), this.__getInfo(info[2])); 17 | 18 | this.id = flightId; 19 | this.icao24bit = this.__getInfo(info[0]); 20 | this.heading = this.__getInfo(info[3]); 21 | this.altitude = this.__getInfo(info[4]); 22 | this.groundSpeed = this.__getInfo(info[5]); 23 | this.squawk = this.__getInfo(info[6]); 24 | this.aircraftCode = this.__getInfo(info[8]); 25 | this.registration = this.__getInfo(info[9]); 26 | this.time = this.__getInfo(info[10]); 27 | this.originAirportIata = this.__getInfo(info[11]); 28 | this.destinationAirportIata = this.__getInfo(info[12]); 29 | this.number = this.__getInfo(info[13]); 30 | this.airlineIata = this.__getInfo(info[13].slice(0, 2)); 31 | this.onGround = this.__getInfo(info[14]); 32 | this.verticalSpeed =this.__getInfo(info[15]); 33 | this.callsign = this.__getInfo(info[16]); 34 | this.airlineIcao = this.__getInfo(info[18]); 35 | } 36 | 37 | /** 38 | * Check one or more flight information. 39 | * 40 | * You can use the prefix "max" or "min" in the parameter 41 | * to compare numeric data with ">" or "<". 42 | * 43 | * Example: checkInfo({minAltitude: 6700, maxAltitude: 13000, airlineIcao: "THY"}) 44 | * 45 | * @param {object} info 46 | * @return {boolean} 47 | */ 48 | checkInfo(info) { 49 | const comparisonFunctions = {"max": Math.max, "min": Math.min}; 50 | 51 | for (let key in info) { 52 | if (!Object.prototype.hasOwnProperty.call(info, key)) { // guard-for-in 53 | continue; 54 | } 55 | 56 | let prefix = key.slice(0, 3); 57 | const value = info[key]; 58 | 59 | // Separate the comparison prefix if it exists. 60 | if ((prefix === "max") || (prefix === "min")) { 61 | key = key[3].toLowerCase() + key.slice(4, key.length); 62 | } 63 | else { 64 | prefix = null; 65 | } 66 | 67 | // Check if the value is greater than or less than the attribute value. 68 | if (this.hasOwnProperty(key) && prefix) { 69 | if (comparisonFunctions[prefix](value, this[key]) !== value) { 70 | return false; 71 | } 72 | } 73 | 74 | // Check if the value is equal. 75 | else if (this.hasOwnProperty(key) && value !== this[key]) { 76 | return false; 77 | } 78 | } 79 | return true; 80 | } 81 | 82 | /** 83 | * Return the formatted altitude, with the unit of measure. 84 | * 85 | * @return {string} 86 | */ 87 | getAltitude() { 88 | return this.altitude.toString() + " ft"; 89 | } 90 | 91 | /** 92 | * Return the formatted flight level, with the unit of measure. 93 | * 94 | * @return {string} 95 | */ 96 | getFlightLevel() { 97 | if (this.altitude >= 10000) { 98 | return this.altitude.toString().slice(0, 3) + " FL"; 99 | } 100 | return this.getAltitude(); 101 | } 102 | 103 | /** 104 | * Return the formatted ground speed, with the unit of measure. 105 | * 106 | * @return {string} 107 | */ 108 | getGroundSpeed() { 109 | const sufix = this.groundSpeed > 1 ? "s" : ""; 110 | return this.groundSpeed.toString() + " kt" + sufix; 111 | } 112 | 113 | /** 114 | * Return the formatted heading, with the unit of measure. 115 | * 116 | * @return {string} 117 | */ 118 | getHeading() { 119 | return this.heading.toString() + "°"; 120 | } 121 | 122 | /** 123 | * Return the formatted vertical speed, with the unit of measure. 124 | * 125 | * @return {string} 126 | */ 127 | getVerticalSpeed() { 128 | return this.verticalSpeed.toString() + " fpm"; 129 | } 130 | 131 | /** 132 | * Set flight details to the instance. Use FlightRadar24API.getFlightDetails(...) method to get it. 133 | * 134 | * @param {object} flightDetails 135 | * @return {undefined} 136 | */ 137 | setFlightDetails(flightDetails) { 138 | // Get aircraft data. 139 | const aircraft = flightDetails["aircraft"]; 140 | 141 | // Get airline data. 142 | const airline = flightDetails?.["airline"]; 143 | 144 | // Get airport data. 145 | const airport = flightDetails?.["airport"]; 146 | 147 | // Get destination data. 148 | const destAirport = airport?.["destination"]; 149 | const destAirportCode = destAirport?.["code"]; 150 | const destAirportInfo = destAirport?.["info"]; 151 | const destAirportPosition = destAirport?.["position"]; 152 | const destAirportCountry = destAirportPosition?.["country"]; 153 | const destAirportTimezone = destAirport?.["timezone"]; 154 | 155 | // Get origin data. 156 | const origAirport = airport?.["origin"]; 157 | const origAirportCode = origAirport?.["code"]; 158 | const origAirportInfo = origAirport?.["info"]; 159 | const origAirportPosition = origAirport?.["position"]; 160 | const origAirportCountry = origAirportPosition?.["country"]; 161 | const origAirportTimezone = origAirport?.["timezone"]; 162 | 163 | // Get flight history. 164 | const history = flightDetails?.["flightHistory"]; 165 | 166 | // Get flight status. 167 | const status = flightDetails?.["status"]; 168 | 169 | // Aircraft information. 170 | this.aircraftAge = this.__getInfo(aircraft?.["age"]); 171 | this.aircraftCountryId = this.__getInfo(aircraft?.["countryId"]); 172 | this.aircraftHistory = this.__getInfo(history?.["aircraft"], []); 173 | this.aircraftImages = this.__getInfo(aircraft?.["images"], []); 174 | this.aircraftModel = this.__getInfo(aircraft?.["model"]?.["text"]); 175 | 176 | // Airline information. 177 | this.airlineName = this.__getInfo(airline?.["name"]); 178 | this.airlineShortName = this.__getInfo(airline?.["short"]); 179 | 180 | // Destination airport position. 181 | this.destinationAirportAltitude = this.__getInfo(destAirportPosition?.["altitude"]); 182 | this.destinationAirportCountryCode = this.__getInfo(destAirportCountry?.["code"]); 183 | this.destinationAirportCountryName = this.__getInfo(destAirportCountry?.["name"]); 184 | this.destinationAirportLatitude = this.__getInfo(destAirportPosition?.["latitude"]); 185 | this.destinationAirportLongitude = this.__getInfo(destAirportPosition?.["longitude"]); 186 | 187 | // Destination airport information. 188 | this.destinationAirportIcao = this.__getInfo(destAirportCode?.["icao"]); 189 | this.destinationAirportBaggage = this.__getInfo(destAirportInfo?.["baggage"]); 190 | this.destinationAirportGate = this.__getInfo(destAirportInfo?.["gate"]); 191 | this.destinationAirportName = this.__getInfo(destAirport?.["name"]); 192 | this.destinationAirportTerminal = this.__getInfo(destAirportInfo?.["terminal"]); 193 | this.destinationAirportVisible = this.__getInfo(destAirport?.["visible"]); 194 | this.destinationAirportWebsite = this.__getInfo(destAirport?.["website"]); 195 | 196 | // Destination airport timezone. 197 | this.destinationAirportTimezoneAbbr = this.__getInfo(destAirportTimezone?.["abbr"]); 198 | this.destinationAirportTimezoneAbbrName = this.__getInfo(destAirportTimezone?.["abbrName"]); 199 | this.destinationAirportTimezoneName = this.__getInfo(destAirportTimezone?.["name"]); 200 | this.destinationAirportTimezoneOffset = this.__getInfo(destAirportTimezone?.["offset"]); 201 | this.destinationAirportTimezoneOffsetHours = this.__getInfo(destAirportTimezone?.["offsetHours"]); 202 | 203 | // Origin airport position. 204 | this.originAirportAltitude = this.__getInfo(origAirportPosition?.["altitude"]); 205 | this.originAirportCountryCode = this.__getInfo(origAirportCountry?.["code"]); 206 | this.originAirportCountryName = this.__getInfo(origAirportCountry?.["name"]); 207 | this.originAirportLatitude = this.__getInfo(origAirportPosition?.["latitude"]); 208 | this.originAirportLongitude = this.__getInfo(origAirportPosition?.["longitude"]); 209 | 210 | // Origin airport information. 211 | this.originAirportIcao = this.__getInfo(origAirportCode?.["icao"]); 212 | this.originAirportBaggage = this.__getInfo(origAirportInfo?.["baggage"]); 213 | this.originAirportGate = this.__getInfo(origAirportInfo?.["gate"]); 214 | this.originAirportName = this.__getInfo(origAirport?.["name"]); 215 | this.originAirportTerminal = this.__getInfo(origAirportInfo?.["terminal"]); 216 | this.originAirportVisible = this.__getInfo(origAirport?.["visible"]); 217 | this.originAirportWebsite = this.__getInfo(origAirport?.["website"]); 218 | 219 | // Origin airport timezone. 220 | this.originAirportTimezoneAbbr = this.__getInfo(origAirportTimezone?.["abbr"]); 221 | this.originAirportTimezoneAbbrName = this.__getInfo(origAirportTimezone?.["abbrName"]); 222 | this.originAirportTimezoneName = this.__getInfo(origAirportTimezone?.["name"]); 223 | this.originAirportTimezoneOffset = this.__getInfo(origAirportTimezone?.["offset"]); 224 | this.originAirportTimezoneOffsetHours = this.__getInfo(origAirportTimezone?.["offsetHours"]); 225 | 226 | // Flight status. 227 | this.statusIcon = this.__getInfo(status?.["icon"]); 228 | this.statusText = this.__getInfo(status?.["text"]); 229 | 230 | // Time details. 231 | this.timeDetails = this.__getInfo(flightDetails?.["time"], {}); 232 | 233 | // Flight trail. 234 | this.trail = this.__getInfo(flightDetails?.["trail"], []); 235 | } 236 | } 237 | 238 | module.exports = Flight; 239 | -------------------------------------------------------------------------------- /python/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for FlightRadarAPI Python package 2 | 3 | # Variables 4 | PACKAGE_NAME = FlightRadarAPI 5 | PYTHON = python3 6 | PIP = pip3 7 | BUILD_DIR = build 8 | DIST_DIR = dist 9 | EGG_INFO = $(PACKAGE_NAME).egg-info 10 | VENV_DIR = venv 11 | TEST_DIR = tests 12 | 13 | # Colors for output 14 | GREEN = \033[0;32m 15 | YELLOW = \033[0;33m 16 | RED = \033[0;31m 17 | BLUE = \033[0;34m 18 | NC = \033[0m # No Color 19 | 20 | # Default target 21 | .PHONY: help 22 | help: 23 | @echo "$(GREEN)FlightRadarAPI Python package Makefile$(NC)" 24 | @echo "" 25 | @echo "Available targets:" 26 | @echo " $(YELLOW)install$(NC) - Install package in development mode" 27 | @echo " $(YELLOW)install-deps$(NC) - Install dependencies" 28 | @echo " $(YELLOW)install-dev$(NC) - Install development dependencies" 29 | @echo " $(YELLOW)test$(NC) - Run tests" 30 | @echo " $(YELLOW)test-verbose$(NC) - Run tests with verbose output" 31 | @echo " $(YELLOW)test-coverage$(NC) - Run tests with coverage report" 32 | @echo " $(YELLOW)lint$(NC) - Run linter (flake8)" 33 | @echo " $(YELLOW)lint-fix$(NC) - Run auto-formatter (black)" 34 | @echo " $(YELLOW)type-check$(NC) - Run type checker (mypy)" 35 | @echo " $(YELLOW)clean$(NC) - Clean build artifacts" 36 | @echo " $(YELLOW)build$(NC) - Build package" 37 | @echo " $(YELLOW)build-wheel$(NC) - Build wheel package" 38 | @echo " $(YELLOW)build-sdist$(NC) - Build source distribution" 39 | @echo " $(YELLOW)validate$(NC) - Validate package" 40 | @echo " $(YELLOW)publish$(NC) - Publish to PyPI" 41 | @echo " $(YELLOW)publish-test$(NC) - Publish to Test PyPI" 42 | @echo " $(YELLOW)version$(NC) - Show current version" 43 | @echo " $(YELLOW)check-deps$(NC) - Check for outdated dependencies" 44 | @echo " $(YELLOW)update-deps$(NC) - Update dependencies" 45 | @echo " $(YELLOW)security$(NC) - Run security audit" 46 | @echo " $(YELLOW)docs$(NC) - Generate documentation" 47 | @echo " $(YELLOW)venv$(NC) - Create virtual environment" 48 | @echo " $(YELLOW)venv-activate$(NC) - Show command to activate venv" 49 | @echo " $(YELLOW)all$(NC) - Run full pipeline (install, lint, test, build)" 50 | 51 | # Create virtual environment 52 | .PHONY: venv 53 | venv: 54 | @echo "$(GREEN)Creating virtual environment...$(NC)" 55 | $(PYTHON) -m venv $(VENV_DIR) 56 | @echo "$(GREEN)Virtual environment created in $(VENV_DIR)$(NC)" 57 | @echo "$(YELLOW)To activate: source $(VENV_DIR)/bin/activate$(NC)" 58 | 59 | # Show activation command 60 | .PHONY: venv-activate 61 | venv-activate: 62 | @echo "$(YELLOW)To activate virtual environment run:$(NC)" 63 | @echo "source $(VENV_DIR)/bin/activate" 64 | 65 | # Install dependencies 66 | .PHONY: install-deps 67 | install-deps: 68 | @echo "$(GREEN)Installing dependencies...$(NC)" 69 | $(PIP) install -r requirements.txt 70 | @echo "$(GREEN)Dependencies installed successfully!$(NC)" 71 | 72 | # Install development dependencies 73 | .PHONY: install-dev 74 | install-dev: 75 | @echo "$(GREEN)Installing development dependencies...$(NC)" 76 | $(PIP) install -r requirements.txt 77 | $(PIP) install pytest pytest-cov flake8 black mypy twine build hatch 78 | @echo "$(GREEN)Development dependencies installed successfully!$(NC)" 79 | 80 | # Install package in development mode 81 | .PHONY: install 82 | install: install-deps 83 | @echo "$(GREEN)Installing package in development mode...$(NC)" 84 | $(PIP) install -e . 85 | @echo "$(GREEN)Package installed successfully!$(NC)" 86 | 87 | # Run tests 88 | .PHONY: test 89 | test: 90 | @echo "$(GREEN)Running tests...$(NC)" 91 | $(PYTHON) -m pytest $(TEST_DIR) -v 92 | @echo "$(GREEN)Tests completed!$(NC)" 93 | 94 | # Run tests with verbose output 95 | .PHONY: test-verbose 96 | test-verbose: 97 | @echo "$(GREEN)Running tests with verbose output...$(NC)" 98 | $(PYTHON) -m pytest $(TEST_DIR) -v -s 99 | @echo "$(GREEN)Verbose tests completed!$(NC)" 100 | 101 | # Run tests with coverage 102 | .PHONY: test-coverage 103 | test-coverage: 104 | @echo "$(GREEN)Running tests with coverage...$(NC)" 105 | $(PYTHON) -m pytest $(TEST_DIR) --cov=$(PACKAGE_NAME) --cov-report=html --cov-report=term 106 | @echo "$(GREEN)Coverage report generated!$(NC)" 107 | @echo "$(YELLOW)HTML report available at htmlcov/index.html$(NC)" 108 | 109 | # Run linter 110 | .PHONY: lint 111 | lint: 112 | @echo "$(GREEN)Running linter (flake8)...$(NC)" 113 | $(PYTHON) -m flake8 $(PACKAGE_NAME) $(TEST_DIR) 114 | @echo "$(GREEN)Linting completed!$(NC)" 115 | 116 | # Run auto-formatter 117 | .PHONY: lint-fix 118 | lint-fix: 119 | @echo "$(GREEN)Running auto-formatter (black)...$(NC)" 120 | $(PYTHON) -m black $(PACKAGE_NAME) $(TEST_DIR) 121 | @echo "$(GREEN)Code formatting completed!$(NC)" 122 | 123 | # Run type checker 124 | .PHONY: type-check 125 | type-check: 126 | @echo "$(GREEN)Running type checker (mypy)...$(NC)" 127 | $(PYTHON) -m mypy $(PACKAGE_NAME) --ignore-missing-imports 128 | @echo "$(GREEN)Type checking completed!$(NC)" 129 | 130 | # Clean build artifacts 131 | .PHONY: clean 132 | clean: 133 | @echo "$(GREEN)Cleaning build artifacts...$(NC)" 134 | rm -rf $(BUILD_DIR) 135 | rm -rf $(DIST_DIR) 136 | rm -rf $(EGG_INFO) 137 | rm -rf .pytest_cache 138 | rm -rf htmlcov 139 | rm -rf .coverage 140 | rm -rf .mypy_cache 141 | find . -type d -name "__pycache__" -exec rm -rf {} + 142 | find . -type f -name "*.pyc" -delete 143 | find . -type f -name "*.pyo" -delete 144 | find . -type f -name "*.pyd" -delete 145 | find . -type f -name ".coverage" -delete 146 | @echo "$(GREEN)Cleanup completed!$(NC)" 147 | 148 | # Build package 149 | .PHONY: build 150 | build: clean 151 | @echo "$(GREEN)Building package...$(NC)" 152 | $(PYTHON) -m build 153 | @echo "$(GREEN)Package built successfully!$(NC)" 154 | 155 | # Build wheel package only 156 | .PHONY: build-wheel 157 | build-wheel: clean 158 | @echo "$(GREEN)Building wheel package...$(NC)" 159 | $(PYTHON) -m build --wheel 160 | @echo "$(GREEN)Wheel package built successfully!$(NC)" 161 | 162 | # Build source distribution only 163 | .PHONY: build-sdist 164 | build-sdist: clean 165 | @echo "$(GREEN)Building source distribution...$(NC)" 166 | $(PYTHON) -m build --sdist 167 | @echo "$(GREEN)Source distribution built successfully!$(NC)" 168 | 169 | # Validate package 170 | .PHONY: validate 171 | validate: build 172 | @echo "$(GREEN)Validating package...$(NC)" 173 | $(PYTHON) -m twine check $(DIST_DIR)/* 174 | @echo "$(GREEN)Package validation completed!$(NC)" 175 | 176 | # Publish to PyPI 177 | .PHONY: publish 178 | publish: validate 179 | @echo "$(YELLOW)Are you sure you want to publish to PyPI? [y/N]$(NC)" && read ans && [ $${ans:-N} = y ] 180 | @echo "$(GREEN)Publishing to PyPI...$(NC)" 181 | $(PYTHON) -m twine upload $(DIST_DIR)/* 182 | @echo "$(GREEN)Package published successfully to PyPI!$(NC)" 183 | 184 | # Publish to Test PyPI 185 | .PHONY: publish-test 186 | publish-test: validate 187 | @echo "$(GREEN)Publishing to Test PyPI...$(NC)" 188 | $(PYTHON) -m twine upload --repository testpypi $(DIST_DIR)/* 189 | @echo "$(GREEN)Package published successfully to Test PyPI!$(NC)" 190 | 191 | # Show current version 192 | .PHONY: version 193 | version: 194 | @echo "$(GREEN)Current package version:$(NC)" 195 | @$(PYTHON) -c "import $(PACKAGE_NAME); print($(PACKAGE_NAME).__version__)" 196 | 197 | # Check for outdated dependencies 198 | .PHONY: check-deps 199 | check-deps: 200 | @echo "$(GREEN)Checking for outdated dependencies...$(NC)" 201 | $(PIP) list --outdated 202 | 203 | # Update dependencies 204 | .PHONY: update-deps 205 | update-deps: 206 | @echo "$(GREEN)Updating dependencies...$(NC)" 207 | $(PIP) install --upgrade -r requirements.txt 208 | @echo "$(GREEN)Dependencies updated!$(NC)" 209 | 210 | # Security audit 211 | .PHONY: security 212 | security: 213 | @echo "$(GREEN)Running security audit...$(NC)" 214 | $(PIP) install safety 215 | safety check 216 | @echo "$(GREEN)Security audit completed!$(NC)" 217 | 218 | # Generate documentation 219 | .PHONY: docs 220 | docs: 221 | @echo "$(GREEN)Generating documentation...$(NC)" 222 | @if [ -d "docs" ]; then \ 223 | cd docs && make html; \ 224 | echo "$(GREEN)Documentation generated in docs/_build/html/$(NC)"; \ 225 | else \ 226 | echo "$(YELLOW)No docs directory found. Skipping documentation generation.$(NC)"; \ 227 | fi 228 | 229 | # Full development pipeline 230 | .PHONY: all 231 | all: install lint type-check test build validate 232 | @echo "$(GREEN)Full pipeline completed successfully!$(NC)" 233 | 234 | # Development workflow targets 235 | .PHONY: dev-setup 236 | dev-setup: venv install-dev install 237 | @echo "$(GREEN)Development environment setup completed!$(NC)" 238 | @echo "$(YELLOW)Don't forget to activate the virtual environment:$(NC)" 239 | @echo "source $(VENV_DIR)/bin/activate" 240 | 241 | .PHONY: pre-commit 242 | pre-commit: lint type-check test 243 | @echo "$(GREEN)Pre-commit checks passed!$(NC)" 244 | 245 | .PHONY: pre-publish 246 | pre-publish: all security 247 | @echo "$(GREEN)Pre-publish checks completed!$(NC)" 248 | 249 | # CI/CD targets 250 | .PHONY: ci 251 | ci: install-dev install lint type-check test build validate 252 | @echo "$(GREEN)CI pipeline completed!$(NC)" 253 | 254 | # Show package info 255 | .PHONY: info 256 | info: 257 | @echo "$(GREEN)Package Information:$(NC)" 258 | @echo "Name: $(PACKAGE_NAME)" 259 | @$(PYTHON) -c "import $(PACKAGE_NAME); print('Version:', $(PACKAGE_NAME).__version__)" 2>/dev/null || echo "Version: Not installed" 260 | @echo "Python: $(shell $(PYTHON) --version)" 261 | @echo "Pip: $(shell $(PIP) --version)" 262 | @echo "" 263 | @echo "$(GREEN)Installed packages:$(NC)" 264 | @$(PIP) list | grep -E "($(PACKAGE_NAME)|pytest|flake8|black|mypy|twine|build)" 265 | 266 | # Quick release workflow 267 | .PHONY: release 268 | release: pre-publish publish 269 | @echo "$(GREEN)Release completed!$(NC)" 270 | 271 | .PHONY: release-test 272 | release-test: pre-publish publish-test 273 | @echo "$(GREEN)Test release completed!$(NC)" 274 | 275 | # Install from PyPI (for testing) 276 | .PHONY: install-from-pypi 277 | install-from-pypi: 278 | @echo "$(GREEN)Installing from PyPI...$(NC)" 279 | $(PIP) install $(PACKAGE_NAME) 280 | @echo "$(GREEN)Package installed from PyPI!$(NC)" 281 | 282 | # Install from Test PyPI (for testing) 283 | .PHONY: install-from-test-pypi 284 | install-from-test-pypi: 285 | @echo "$(GREEN)Installing from Test PyPI...$(NC)" 286 | $(PIP) install --index-url https://test.pypi.org/simple/ $(PACKAGE_NAME) 287 | @echo "$(GREEN)Package installed from Test PyPI!$(NC)" 288 | 289 | # Uninstall package 290 | .PHONY: uninstall 291 | uninstall: 292 | @echo "$(GREEN)Uninstalling package...$(NC)" 293 | $(PIP) uninstall $(PACKAGE_NAME) -y 294 | @echo "$(GREEN)Package uninstalled!$(NC)" 295 | -------------------------------------------------------------------------------- /python/FlightRadar24/entities/flight.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Any, Dict, List, Optional 4 | from .entity import Entity 5 | 6 | 7 | class Flight(Entity): 8 | """ 9 | Flight representation. 10 | """ 11 | def __init__(self, flight_id: str, info: List[Any]): 12 | """ 13 | Constructor of the Flight class. 14 | 15 | :param flight_id: The flight ID specifically used by FlightRadar24 16 | :param info: Dictionary with received data from FlightRadar24 17 | """ 18 | super().__init__( 19 | latitude=self.__get_info(info[1]), 20 | longitude=self.__get_info(info[2]) 21 | ) 22 | 23 | self.id = flight_id 24 | self.icao_24bit = self.__get_info(info[0]) 25 | self.heading = self.__get_info(info[3]) 26 | self.altitude = self.__get_info(info[4]) 27 | self.ground_speed = self.__get_info(info[5]) 28 | self.squawk = self.__get_info(info[6]) 29 | self.aircraft_code = self.__get_info(info[8]) 30 | self.registration = self.__get_info(info[9]) 31 | self.time = self.__get_info(info[10]) 32 | self.origin_airport_iata = self.__get_info(info[11]) 33 | self.destination_airport_iata = self.__get_info(info[12]) 34 | self.number = self.__get_info(info[13]) 35 | self.airline_iata = self.__get_info(info[13][:2]) 36 | self.on_ground = self.__get_info(info[14]) 37 | self.vertical_speed = self.__get_info(info[15]) 38 | self.callsign = self.__get_info(info[16]) 39 | self.airline_icao = self.__get_info(info[18]) 40 | 41 | def __repr__(self) -> str: 42 | return self.__str__() 43 | 44 | def __str__(self) -> str: 45 | template = "<({}) {} - Altitude: {} - Ground Speed: {} - Heading: {}>" 46 | return template.format(self.aircraft_code, self.registration, self.altitude, self.ground_speed, self.heading) 47 | 48 | def __get_info(self, info: Any, default: Optional[Any] = None) -> Any: 49 | default = default if default is not None else self._default_text 50 | return info if info is not None and info != self._default_text else default 51 | 52 | def check_info(self, **info: Any) -> bool: 53 | """ 54 | Check one or more flight information. 55 | 56 | You can use the prefix "max_" or "min_" in the parameter 57 | to compare numeric data with ">" or "<". 58 | 59 | Example: check_info(min_altitude = 6700, max_altitude = 13000, airline_icao = "THY") 60 | """ 61 | 62 | comparison_functions = {"max": max, "min": min} 63 | 64 | for key, value in info.items(): 65 | 66 | # Separate the comparison prefix if it exists. 67 | prefix, key = key.split("_", maxsplit=1) if key[:4] == "max_" or key[:4] == "min_" else (None, key) 68 | 69 | # Check if the value is greater than or less than the attribute value. 70 | if prefix and key in self.__dict__: 71 | if comparison_functions[prefix](value, self.__dict__[key]) != value: return False 72 | 73 | # Check if the value is equal. 74 | elif key in self.__dict__ and value != self.__dict__[key]: return False 75 | 76 | return True 77 | 78 | def get_altitude(self) -> str: 79 | """ 80 | Return the formatted altitude, with the unit of measure. 81 | """ 82 | return "{} ft".format(self.altitude) 83 | 84 | def get_flight_level(self) -> str: 85 | """ 86 | Return the formatted flight level, with the unit of measure. 87 | """ 88 | return str(self.altitude)[:3] + " FL" if self.altitude >= 10000 else self.get_altitude() 89 | 90 | def get_ground_speed(self) -> str: 91 | """ 92 | Return the formatted ground speed, with the unit of measure. 93 | """ 94 | return "{} kt".format(self.ground_speed) + ("s" if self.ground_speed > 1 else "") 95 | 96 | def get_heading(self) -> str: 97 | """ 98 | Return the formatted heading, with the unit of measure. 99 | """ 100 | return str(self.heading) + "°" 101 | 102 | def get_vertical_speed(self) -> str: 103 | """ 104 | Return the formatted vertical speed, with the unit of measure. 105 | """ 106 | return "{} fpm".format(self.vertical_speed) 107 | 108 | def set_flight_details(self, flight_details: Dict) -> None: 109 | """ 110 | Set flight details to the instance. Use FlightRadar24API.get_flight_details(...) method to get it. 111 | """ 112 | # Get aircraft data. 113 | aircraft = self.__get_info(flight_details.get("aircraft"), dict()) 114 | 115 | # Get airline data. 116 | airline = self.__get_info(flight_details.get("airline"), dict()) 117 | 118 | # Get airport data. 119 | airport = self.__get_info(flight_details.get("airport"), dict()) 120 | 121 | # Get destination data. 122 | dest_airport = self.__get_info(airport.get("destination"), dict()) 123 | dest_airport_code = self.__get_info(dest_airport.get("code"), dict()) 124 | dest_airport_info = self.__get_info(dest_airport.get("info"), dict()) 125 | dest_airport_position = self.__get_info(dest_airport.get("position"), dict()) 126 | dest_airport_country = self.__get_info(dest_airport_position.get("country"), dict()) 127 | dest_airport_timezone = self.__get_info(dest_airport.get("timezone"), dict()) 128 | 129 | # Get origin data. 130 | orig_airport = self.__get_info(airport.get("origin"), dict()) 131 | orig_airport_code = self.__get_info(orig_airport.get("code"), dict()) 132 | orig_airport_info = self.__get_info(orig_airport.get("info"), dict()) 133 | orig_airport_position = self.__get_info(orig_airport.get("position"), dict()) 134 | orig_airport_country = self.__get_info(orig_airport_position.get("country"), dict()) 135 | orig_airport_timezone = self.__get_info(orig_airport.get("timezone"), dict()) 136 | 137 | # Get flight history. 138 | history = self.__get_info(flight_details.get("flightHistory"), dict()) 139 | 140 | # Get flight status. 141 | status = self.__get_info(flight_details.get("status"), dict()) 142 | 143 | # Aircraft information. 144 | self.aircraft_age = self.__get_info(aircraft.get("age")) 145 | self.aircraft_country_id = self.__get_info(aircraft.get("countryId")) 146 | self.aircraft_history = history.get("aircraft", list()) 147 | self.aircraft_images = aircraft.get("images", list()) 148 | self.aircraft_model = self.__get_info(self.__get_info(aircraft.get("model"), dict()).get("text")) 149 | 150 | # Airline information. 151 | self.airline_name = self.__get_info(airline.get("name")) 152 | self.airline_short_name = self.__get_info(airline.get("short")) 153 | 154 | # Destination airport position. 155 | self.destination_airport_altitude = self.__get_info(dest_airport_position.get("altitude")) 156 | self.destination_airport_country_code = self.__get_info(dest_airport_country.get("code")) 157 | self.destination_airport_country_name = self.__get_info(dest_airport_country.get("name")) 158 | self.destination_airport_latitude = self.__get_info(dest_airport_position.get("latitude")) 159 | self.destination_airport_longitude = self.__get_info(dest_airport_position.get("longitude")) 160 | 161 | # Destination airport information. 162 | self.destination_airport_icao = self.__get_info(dest_airport_code.get("icao")) 163 | self.destination_airport_baggage = self.__get_info(dest_airport_info.get("baggage")) 164 | self.destination_airport_gate = self.__get_info(dest_airport_info.get("gate")) 165 | self.destination_airport_name = self.__get_info(dest_airport.get("name")) 166 | self.destination_airport_terminal = self.__get_info(dest_airport_info.get("terminal")) 167 | self.destination_airport_visible = self.__get_info(dest_airport.get("visible")) 168 | self.destination_airport_website = self.__get_info(dest_airport.get("website")) 169 | 170 | # Destination airport timezone. 171 | self.destination_airport_timezone_abbr = self.__get_info(dest_airport_timezone.get("abbr")) 172 | self.destination_airport_timezone_abbr_name = self.__get_info(dest_airport_timezone.get("abbrName")) 173 | self.destination_airport_timezone_name = self.__get_info(dest_airport_timezone.get("name")) 174 | self.destination_airport_timezone_offset = self.__get_info(dest_airport_timezone.get("offset")) 175 | self.destination_airport_timezone_offset_hours = self.__get_info(dest_airport_timezone.get("offsetHours")) 176 | 177 | # Origin airport position. 178 | self.origin_airport_altitude = self.__get_info(orig_airport_position.get("altitude")) 179 | self.origin_airport_country_code = self.__get_info(orig_airport_country.get("code")) 180 | self.origin_airport_country_name = self.__get_info(orig_airport_country.get("name")) 181 | self.origin_airport_latitude = self.__get_info(orig_airport_position.get("latitude")) 182 | self.origin_airport_longitude = self.__get_info(orig_airport_position.get("longitude")) 183 | 184 | # Origin airport information. 185 | self.origin_airport_icao = self.__get_info(orig_airport_code.get("icao")) 186 | self.origin_airport_baggage = self.__get_info(orig_airport_info.get("baggage")) 187 | self.origin_airport_gate = self.__get_info(orig_airport_info.get("gate")) 188 | self.origin_airport_name = self.__get_info(orig_airport.get("name")) 189 | self.origin_airport_terminal = self.__get_info(orig_airport_info.get("terminal")) 190 | self.origin_airport_visible = self.__get_info(orig_airport.get("visible")) 191 | self.origin_airport_website = self.__get_info(orig_airport.get("website")) 192 | 193 | # Origin airport timezone. 194 | self.origin_airport_timezone_abbr = self.__get_info(orig_airport_timezone.get("abbr")) 195 | self.origin_airport_timezone_abbr_name = self.__get_info(orig_airport_timezone.get("abbrName")) 196 | self.origin_airport_timezone_name = self.__get_info(orig_airport_timezone.get("name")) 197 | self.origin_airport_timezone_offset = self.__get_info(orig_airport_timezone.get("offset")) 198 | self.origin_airport_timezone_offset_hours = self.__get_info(orig_airport_timezone.get("offsetHours")) 199 | 200 | # Flight status. 201 | self.status_icon = self.__get_info(status.get("icon")) 202 | self.status_text = self.__get_info(status.get("text")) 203 | 204 | # Time details. 205 | self.time_details = self.__get_info(flight_details.get("time"), dict()) 206 | 207 | # Flight trail. 208 | self.trail = flight_details.get("trail", list()) 209 | -------------------------------------------------------------------------------- /python/FlightRadar24/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from abc import ABC 4 | from enum import Enum 5 | from .zones import static_zones 6 | 7 | 8 | class Core(ABC): 9 | 10 | # Base URLs. 11 | api_flightradar_base_url = "https://api.flightradar24.com/common/v1" 12 | cdn_flightradar_base_url = "https://cdn.flightradar24.com" 13 | flightradar_base_url = "https://www.flightradar24.com" 14 | data_live_base_url = "https://data-live.flightradar24.com" 15 | data_cloud_base_url = "https://data-cloud.flightradar24.com" 16 | 17 | # User login URL. 18 | user_login_url = flightradar_base_url + "/user/login" 19 | user_logout_url = flightradar_base_url + "/user/logout" 20 | 21 | # Search data URL 22 | search_data_url = flightradar_base_url + "/v1/search/web/find?query={}&limit={}" 23 | 24 | # Flights data URLs. 25 | real_time_flight_tracker_data_url = data_cloud_base_url + "/zones/fcgi/feed.js" 26 | flight_data_url = data_live_base_url + "/clickhandler/?flight={}" 27 | 28 | # Historical data URL. 29 | historical_data_url = flightradar_base_url + "/download/?flight={}&file={}&trailLimit=0&history={}" 30 | 31 | # Airports data URLs. 32 | api_airport_data_url = api_flightradar_base_url + "/airport.json" 33 | airport_data_url = flightradar_base_url + "/airports/traffic-stats/?airport={}" 34 | airports_data_url = flightradar_base_url + "/data/airports" 35 | 36 | # Airlines data URL. 37 | airlines_data_url = flightradar_base_url + "/data/airlines" 38 | 39 | # Zones data URL. 40 | zones_data_url = flightradar_base_url + "/js/zones.js.php" 41 | 42 | # Weather data URL. 43 | volcanic_eruption_data_url = flightradar_base_url + "/weather/volcanic" 44 | 45 | # Most tracked URL 46 | most_tracked_url = flightradar_base_url + "/flights/most-tracked" 47 | 48 | # Airport disruptions URL. 49 | airport_disruptions_url = flightradar_base_url + "/webapi/v1/airport-disruptions" 50 | 51 | # Bookmarks URL. 52 | bookmarks_url = flightradar_base_url + "/webapi/v1/bookmarks" 53 | 54 | # Country flag image URL. 55 | country_flag_url = flightradar_base_url + "/static/images/data/flags-small/{}.svg" 56 | 57 | # Airline logo image URL. 58 | airline_logo_url = cdn_flightradar_base_url + "/assets/airlines/logotypes/{}_{}.png" 59 | alternative_airline_logo_url = flightradar_base_url + "/static/images/data/operators/{}_logo0.png" 60 | 61 | static_zones = static_zones 62 | 63 | headers = { 64 | "accept-encoding": "gzip, br", 65 | "accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7", 66 | "cache-control": "max-age=0", 67 | "origin": "https://www.flightradar24.com", 68 | "referer": "https://www.flightradar24.com/", 69 | "sec-fetch-dest": "empty", 70 | "sec-fetch-mode": "cors", 71 | "sec-fetch-site": "same-site", 72 | "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" 73 | } 74 | 75 | json_headers = headers.copy() 76 | json_headers["accept"] = "application/json" 77 | 78 | image_headers = headers.copy() 79 | image_headers["accept"] = "image/gif, image/jpg, image/jpeg, image/png" 80 | 81 | html_headers = headers.copy() 82 | html_headers["accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" 83 | 84 | 85 | class Countries(Enum): 86 | """ 87 | Enum mapping country names in to their URL-friendly string representations. 88 | """ 89 | AFGHANISTAN = "afghanistan" 90 | ALBANIA = "albania" 91 | ALGERIA = "algeria" 92 | AMERICAN_SAMOA = "american-samoa" 93 | ANGOLA = "angola" 94 | ANGUILLA = "anguilla" 95 | ANTARCTICA = "antarctica" 96 | ANTIGUA_AND_BARBUDA = "antigua-and-barbuda" 97 | ARGENTINA = "argentina" 98 | ARMENIA = "armenia" 99 | ARUBA = "aruba" 100 | AUSTRALIA = "australia" 101 | AUSTRIA = "austria" 102 | AZERBAIJAN = "azerbaijan" 103 | BAHAMAS = "bahamas" 104 | BAHRAIN = "bahrain" 105 | BANGLADESH = "bangladesh" 106 | BARBADOS = "barbados" 107 | BELARUS = "belarus" 108 | BELGIUM = "belgium" 109 | BELIZE = "belize" 110 | BENIN = "benin" 111 | BERMUDA = "bermuda" 112 | BHUTAN = "bhutan" 113 | BOLIVIA = "bolivia" 114 | BOSNIA_AND_HERZEGOVINA = "bosnia-and-herzegovina" 115 | BOTSWANA = "botswana" 116 | BRAZIL = "brazil" 117 | BRUNEI = "brunei" 118 | BULGARIA = "bulgaria" 119 | BURKINA_FASO = "burkina-faso" 120 | BURUNDI = "burundi" 121 | CAMBODIA = "cambodia" 122 | CAMEROON = "cameroon" 123 | CANADA = "canada" 124 | CAPE_VERDE = "cape-verde" 125 | CAYMAN_ISLANDS = "cayman-islands" 126 | CENTRAL_AFRICAN_REPUBLIC = "central-african-republic" 127 | CHAD = "chad" 128 | CHILE = "chile" 129 | CHINA = "china" 130 | COCOS_KEELING_ISLANDS = "cocos-keeling-islands" 131 | COLOMBIA = "colombia" 132 | COMOROS = "comoros" 133 | CONGO = "congo" 134 | COOK_ISLANDS = "cook-islands" 135 | COSTA_RICA = "costa-rica" 136 | CROATIA = "croatia" 137 | CUBA = "cuba" 138 | CURACAO = "curacao" 139 | CYPRUS = "cyprus" 140 | CZECHIA = "czechia" 141 | DEMOCRATIC_REPUBLIC_OF_THE_CONGO = "democratic-republic-of-the-congo" 142 | DENMARK = "denmark" 143 | DJIBOUTI = "djibouti" 144 | DOMINICA = "dominica" 145 | DOMINICAN_REPUBLIC = "dominican-republic" 146 | ECUADOR = "ecuador" 147 | EGYPT = "egypt" 148 | EL_SALVADOR = "el-salvador" 149 | EQUATORIAL_GUINEA = "equatorial-guinea" 150 | ERITREA = "eritrea" 151 | ESTONIA = "estonia" 152 | ESWATINI = "eswatini" 153 | ETHIOPIA = "ethiopia" 154 | FALKLAND_ISLANDS_MALVINAS = "falkland-islands-malvinas" 155 | FAROE_ISLANDS = "faroe-islands" 156 | FIJI = "fiji" 157 | FINLAND = "finland" 158 | FRANCE = "france" 159 | FRENCH_GUIANA = "french-guiana" 160 | FRENCH_POLYNESIA = "french-polynesia" 161 | GABON = "gabon" 162 | GAMBIA = "gambia" 163 | GEORGIA = "georgia" 164 | GERMANY = "germany" 165 | GHANA = "ghana" 166 | GIBRALTAR = "gibraltar" 167 | GREECE = "greece" 168 | GREENLAND = "greenland" 169 | GRENADA = "grenada" 170 | GUADELOUPE = "guadeloupe" 171 | GUAM = "guam" 172 | GUATEMALA = "guatemala" 173 | GUERNSEY = "guernsey" 174 | GUINEA = "guinea" 175 | GUINEA_BISSAU = "guinea-bissau" 176 | GUYANA = "guyana" 177 | HAITI = "haiti" 178 | HONDURAS = "honduras" 179 | HONG_KONG = "hong-kong" 180 | HUNGARY = "hungary" 181 | ICELAND = "iceland" 182 | INDIA = "india" 183 | INDONESIA = "indonesia" 184 | IRAN = "iran" 185 | IRAQ = "iraq" 186 | IRELAND = "ireland" 187 | ISLE_OF_MAN = "isle-of-man" 188 | ISRAEL = "israel" 189 | ITALY = "italy" 190 | IVORY_COAST = "ivory-coast" 191 | JAMAICA = "jamaica" 192 | JAPAN = "japan" 193 | JERSEY = "jersey" 194 | JORDAN = "jordan" 195 | KAZAKHSTAN = "kazakhstan" 196 | KENYA = "kenya" 197 | KIRIBATI = "kiribati" 198 | KOSOVO = "kosovo" 199 | KUWAIT = "kuwait" 200 | KYRGYZSTAN = "kyrgyzstan" 201 | LAOS = "laos" 202 | LATVIA = "latvia" 203 | LEBANON = "lebanon" 204 | LESOTHO = "lesotho" 205 | LIBERIA = "liberia" 206 | LIBYA = "libya" 207 | LITHUANIA = "lithuania" 208 | LUXEMBOURG = "luxembourg" 209 | MACAO = "macao" 210 | MADAGASCAR = "madagascar" 211 | MALAWI = "malawi" 212 | MALAYSIA = "malaysia" 213 | MALDIVES = "maldives" 214 | MALI = "mali" 215 | MALTA = "malta" 216 | MARSHALL_ISLANDS = "marshall-islands" 217 | MARTINIQUE = "martinique" 218 | MAURITANIA = "mauritania" 219 | MAURITIUS = "mauritius" 220 | MAYOTTE = "mayotte" 221 | MEXICO = "mexico" 222 | MICRONESIA = "micronesia" 223 | MOLDOVA = "moldova" 224 | MONACO = "monaco" 225 | MONGOLIA = "mongolia" 226 | MONTENEGRO = "montenegro" 227 | MONTSERRAT = "montserrat" 228 | MOROCCO = "morocco" 229 | MOZAMBIQUE = "mozambique" 230 | MYANMAR_BURMA = "myanmar-burma" 231 | NAMIBIA = "namibia" 232 | NAURU = "nauru" 233 | NEPAL = "nepal" 234 | NETHERLANDS = "netherlands" 235 | NEW_CALEDONIA = "new-caledonia" 236 | NEW_ZEALAND = "new-zealand" 237 | NICARAGUA = "nicaragua" 238 | NIGER = "niger" 239 | NIGERIA = "nigeria" 240 | NORTH_KOREA = "north-korea" 241 | NORTH_MACEDONIA = "north-macedonia" 242 | NORTHERN_MARIANA_ISLANDS = "northern-mariana-islands" 243 | NORWAY = "norway" 244 | OMAN = "oman" 245 | PAKISTAN = "pakistan" 246 | PALAU = "palau" 247 | PANAMA = "panama" 248 | PAPUA_NEW_GUINEA = "papua-new-guinea" 249 | PARAGUAY = "paraguay" 250 | PERU = "peru" 251 | PHILIPPINES = "philippines" 252 | POLAND = "poland" 253 | PORTUGAL = "portugal" 254 | PUERTO_RICO = "puerto-rico" 255 | QATAR = "qatar" 256 | REUNION = "reunion" 257 | ROMANIA = "romania" 258 | RUSSIA = "russia" 259 | RWANDA = "rwanda" 260 | SAINT_HELENA = "saint-helena" 261 | SAINT_KITTS_AND_NEVIS = "saint-kitts-and-nevis" 262 | SAINT_LUCIA = "saint-lucia" 263 | SAINT_PIERRE_AND_MIQUELON = "saint-pierre-and-miquelon" 264 | SAINT_VINCENT_AND_THE_GRENADINES = "saint-vincent-and-the-grenadines" 265 | SAMOA = "samoa" 266 | SAO_TOME_AND_PRINCIPE = "sao-tome-and-principe" 267 | SAUDI_ARABIA = "saudi-arabia" 268 | SENEGAL = "senegal" 269 | SERBIA = "serbia" 270 | SEYCHELLES = "seychelles" 271 | SIERRA_LEONE = "sierra-leone" 272 | SINGAPORE = "singapore" 273 | SLOVAKIA = "slovakia" 274 | SLOVENIA = "slovenia" 275 | SOLOMON_ISLANDS = "solomon-islands" 276 | SOMALIA = "somalia" 277 | SOUTH_AFRICA = "south-africa" 278 | SOUTH_KOREA = "south-korea" 279 | SOUTH_SUDAN = "south-sudan" 280 | SPAIN = "spain" 281 | SRI_LANKA = "sri-lanka" 282 | SUDAN = "sudan" 283 | SURINAME = "suriname" 284 | SWEDEN = "sweden" 285 | SWITZERLAND = "switzerland" 286 | SYRIA = "syria" 287 | TAIWAN = "taiwan" 288 | TAJIKISTAN = "tajikistan" 289 | TANZANIA = "tanzania" 290 | THAILAND = "thailand" 291 | TIMOR_LESTE_EAST_TIMOR = "timor-leste-east-timor" 292 | TOGO = "togo" 293 | TONGA = "tonga" 294 | TRINIDAD_AND_TOBAGO = "trinidad-and-tobago" 295 | TUNISIA = "tunisia" 296 | TURKEY = "turkey" 297 | TURKMENISTAN = "turkmenistan" 298 | TURKS_AND_CAICOS_ISLANDS = "turks-and-caicos-islands" 299 | TUVALU = "tuvalu" 300 | UGANDA = "uganda" 301 | UKRAINE = "ukraine" 302 | UNITED_ARAB_EMIRATES = "united-arab-emirates" 303 | UNITED_KINGDOM = "united-kingdom" 304 | UNITED_STATES = "united-states" 305 | UNITED_STATES_MINOR_OUTLYING_ISLANDS = "united-states-minor-outlying-islands" 306 | URUGUAY = "uruguay" 307 | UZBEKISTAN = "uzbekistan" 308 | VANUATU = "vanuatu" 309 | VENEZUELA = "venezuela" 310 | VIETNAM = "vietnam" 311 | VIRGIN_ISLANDS_BRITISH = "virgin-islands-british" 312 | VIRGIN_ISLANDS_US = "virgin-islands-us" 313 | WALLIS_AND_FUTUNA = "wallis-and-futuna" 314 | YEMEN = "yemen" 315 | ZAMBIA = "zambia" 316 | ZIMBABWE = "zimbabwe" -------------------------------------------------------------------------------- /nodejs/FlightRadar24/core.js: -------------------------------------------------------------------------------- 1 | const {staticZones} = require("./zones"); 2 | 3 | String.prototype.format = function() { 4 | const args = arguments; 5 | let index = 0; 6 | 7 | return this.replace(/{}/g, function(match, position) { 8 | return (typeof args[index] == "undefined") ? match : args[index++]; 9 | }); 10 | }; 11 | 12 | 13 | /** 14 | * Class which contains all URLs used by the package. 15 | */ 16 | class Core { 17 | /** 18 | * Constructor of the Core class 19 | */ 20 | constructor() { 21 | this.apiFlightradarBaseUrl = "https://api.flightradar24.com/common/v1"; 22 | this.cdnFlightradarBaseUrl = "https://cdn.flightradar24.com"; 23 | this.flightRadarBaseUrl = "https://www.flightradar24.com"; 24 | this.dataLiveBaseUrl = "https://data-live.flightradar24.com"; 25 | this.dataCloudBaseUrl = "https://data-cloud.flightradar24.com"; 26 | 27 | // User login URL. 28 | this.userLoginUrl = this.flightRadarBaseUrl + "/user/login"; 29 | this.userLogoutUrl = this.flightRadarBaseUrl + "/user/logout"; 30 | 31 | // Search data URL 32 | this.searchDataUrl = this.flightRadarBaseUrl + "/v1/search/web/find?query={}&limit={}"; 33 | 34 | // Flights data URLs. 35 | this.realTimeFlightTrackerDataUrl = this.dataCloudBaseUrl + "/zones/fcgi/feed.js"; 36 | this.flightDataUrl = this.dataLiveBaseUrl + "/clickhandler/?flight={}"; 37 | 38 | // Historical data URL. 39 | this.historicalDataUrl = this.flightradarBaseUrl + "/download/?flight={}&file={}&trailLimit=0&history={}"; 40 | 41 | // Airports data URLs. 42 | this.apiAirportDataUrl = this.apiFlightradarBaseUrl + "/airport.json"; 43 | this.airportDataUrl = this.flightRadarBaseUrl + "/airports/traffic-stats/?airport={}"; 44 | this.airportsDataUrl = this.flightRadarBaseUrl + "/data/airports"; 45 | 46 | // Airlines data URL. 47 | this.airlinesDataUrl = this.flightRadarBaseUrl + "/data/airlines"; 48 | 49 | // Zones data URL. 50 | this.zonesDataUrl = this.flightRadarBaseUrl + "/js/zones.js.php"; 51 | 52 | // Weather data URL. 53 | this.volcanicEruptionDataUrl = this.flightRadarBaseUrl + "/weather/volcanic"; 54 | 55 | // Most tracked URL 56 | this.mostTrackedUrl = this.flightRadarBaseUrl + "/flights/most-tracked"; 57 | 58 | // Airport disruptions URL. 59 | this.airportDisruptionsUrl = this.flightRadarBaseUrl + "/webapi/v1/airport-disruptions"; 60 | 61 | // Bookmarks URL. 62 | this.bookmarksUrl = this.flightRadarBaseUrl + "/webapi/v1/bookmarks"; 63 | 64 | // Country flag image URL. 65 | this.countryFlagUrl = this.flightRadarBaseUrl + "/static/images/data/flags-small/{}.svg"; 66 | 67 | // Airline logo image URL. 68 | this.airlineLogoUrl = this.cdnFlightradarBaseUrl + "/assets/airlines/logotypes/{}_{}.png"; 69 | this.alternativeAirlineLogoUrl = this.flightRadarBaseUrl + "/static/images/data/operators/{}_logo0.png"; 70 | 71 | this.staticZones = staticZones; 72 | 73 | this.headers = { 74 | "accept-encoding": "gzip, br", 75 | "accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7", 76 | "cache-control": "max-age=0", 77 | "origin": "https://www.flightradar24.com", 78 | "referer": "https://www.flightradar24.com/", 79 | "sec-fetch-dest": "empty", 80 | "sec-fetch-mode": "cors", 81 | "sec-fetch-site": "same-site", 82 | "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", 83 | }; 84 | 85 | this.jsonHeaders = {accept: "application/json", ...this.headers}; 86 | 87 | this.imageHeaders = {accept: "image/gif, image/jpg, image/jpeg, image/png", ...this.headers}; 88 | 89 | this.htmlHeaders = {accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", ...this.headers}; 90 | } 91 | } 92 | 93 | /** 94 | * Enum mapping country names to their URL-friendly string representations. 95 | */ 96 | const Countries = { 97 | AFGHANISTAN: "afghanistan", 98 | ALBANIA: "albania", 99 | ALGERIA: "algeria", 100 | AMERICAN_SAMOA: "american-samoa", 101 | ANGOLA: "angola", 102 | ANGUILLA: "anguilla", 103 | ANTARCTICA: "antarctica", 104 | ANTIGUA_AND_BARBUDA: "antigua-and-barbuda", 105 | ARGENTINA: "argentina", 106 | ARMENIA: "armenia", 107 | ARUBA: "aruba", 108 | AUSTRALIA: "australia", 109 | AUSTRIA: "austria", 110 | AZERBAIJAN: "azerbaijan", 111 | BAHAMAS: "bahamas", 112 | BAHRAIN: "bahrain", 113 | BANGLADESH: "bangladesh", 114 | BARBADOS: "barbados", 115 | BELARUS: "belarus", 116 | BELGIUM: "belgium", 117 | BELIZE: "belize", 118 | BENIN: "benin", 119 | BERMUDA: "bermuda", 120 | BHUTAN: "bhutan", 121 | BOLIVIA: "bolivia", 122 | BOSNIA_AND_HERZEGOVINA: "bosnia-and-herzegovina", 123 | BOTSWANA: "botswana", 124 | BRAZIL: "brazil", 125 | BRUNEI: "brunei", 126 | BULGARIA: "bulgaria", 127 | BURKINA_FASO: "burkina-faso", 128 | BURUNDI: "burundi", 129 | CAMBODIA: "cambodia", 130 | CAMEROON: "cameroon", 131 | CANADA: "canada", 132 | CAPE_VERDE: "cape-verde", 133 | CAYMAN_ISLANDS: "cayman-islands", 134 | CENTRAL_AFRICAN_REPUBLIC: "central-african-republic", 135 | CHAD: "chad", 136 | CHILE: "chile", 137 | CHINA: "china", 138 | COCOS_KEELING_ISLANDS: "cocos-keeling-islands", 139 | COLOMBIA: "colombia", 140 | COMOROS: "comoros", 141 | CONGO: "congo", 142 | COOK_ISLANDS: "cook-islands", 143 | COSTA_RICA: "costa-rica", 144 | CROATIA: "croatia", 145 | CUBA: "cuba", 146 | CURACAO: "curacao", 147 | CYPRUS: "cyprus", 148 | CZECHIA: "czechia", 149 | DEMOCRATIC_REPUBLIC_OF_THE_CONGO: "democratic-republic-of-the-congo", 150 | DENMARK: "denmark", 151 | DJIBOUTI: "djibouti", 152 | DOMINICA: "dominica", 153 | DOMINICAN_REPUBLIC: "dominican-republic", 154 | ECUADOR: "ecuador", 155 | EGYPT: "egypt", 156 | EL_SALVADOR: "el-salvador", 157 | EQUATORIAL_GUINEA: "equatorial-guinea", 158 | ERITREA: "eritrea", 159 | ESTONIA: "estonia", 160 | ESWATINI: "eswatini", 161 | ETHIOPIA: "ethiopia", 162 | FALKLAND_ISLANDS_MALVINAS: "falkland-islands-malvinas", 163 | FAROE_ISLANDS: "faroe-islands", 164 | FIJI: "fiji", 165 | FINLAND: "finland", 166 | FRANCE: "france", 167 | FRENCH_GUIANA: "french-guiana", 168 | FRENCH_POLYNESIA: "french-polynesia", 169 | GABON: "gabon", 170 | GAMBIA: "gambia", 171 | GEORGIA: "georgia", 172 | GERMANY: "germany", 173 | GHANA: "ghana", 174 | GIBRALTAR: "gibraltar", 175 | GREECE: "greece", 176 | GREENLAND: "greenland", 177 | GRENADA: "grenada", 178 | GUADELOUPE: "guadeloupe", 179 | GUAM: "guam", 180 | GUATEMALA: "guatemala", 181 | GUERNSEY: "guernsey", 182 | GUINEA: "guinea", 183 | GUINEA_BISSAU: "guinea-bissau", 184 | GUYANA: "guyana", 185 | HAITI: "haiti", 186 | HONDURAS: "honduras", 187 | HONG_KONG: "hong-kong", 188 | HUNGARY: "hungary", 189 | ICELAND: "iceland", 190 | INDIA: "india", 191 | INDONESIA: "indonesia", 192 | IRAN: "iran", 193 | IRAQ: "iraq", 194 | IRELAND: "ireland", 195 | ISLE_OF_MAN: "isle-of-man", 196 | ISRAEL: "israel", 197 | ITALY: "italy", 198 | IVORY_COAST: "ivory-coast", 199 | JAMAICA: "jamaica", 200 | JAPAN: "japan", 201 | JERSEY: "jersey", 202 | JORDAN: "jordan", 203 | KAZAKHSTAN: "kazakhstan", 204 | KENYA: "kenya", 205 | KIRIBATI: "kiribati", 206 | KOSOVO: "kosovo", 207 | KUWAIT: "kuwait", 208 | KYRGYZSTAN: "kyrgyzstan", 209 | LAOS: "laos", 210 | LATVIA: "latvia", 211 | LEBANON: "lebanon", 212 | LESOTHO: "lesotho", 213 | LIBERIA: "liberia", 214 | LIBYA: "libya", 215 | LITHUANIA: "lithuania", 216 | LUXEMBOURG: "luxembourg", 217 | MACAO: "macao", 218 | MADAGASCAR: "madagascar", 219 | MALAWI: "malawi", 220 | MALAYSIA: "malaysia", 221 | MALDIVES: "maldives", 222 | MALI: "mali", 223 | MALTA: "malta", 224 | MARSHALL_ISLANDS: "marshall-islands", 225 | MARTINIQUE: "martinique", 226 | MAURITANIA: "mauritania", 227 | MAURITIUS: "mauritius", 228 | MAYOTTE: "mayotte", 229 | MEXICO: "mexico", 230 | MICRONESIA: "micronesia", 231 | MOLDOVA: "moldova", 232 | MONACO: "monaco", 233 | MONGOLIA: "mongolia", 234 | MONTENEGRO: "montenegro", 235 | MONTSERRAT: "montserrat", 236 | MOROCCO: "morocco", 237 | MOZAMBIQUE: "mozambique", 238 | MYANMAR_BURMA: "myanmar-burma", 239 | NAMIBIA: "namibia", 240 | NAURU: "nauru", 241 | NEPAL: "nepal", 242 | NETHERLANDS: "netherlands", 243 | NEW_CALEDONIA: "new-caledonia", 244 | NEW_ZEALAND: "new-zealand", 245 | NICARAGUA: "nicaragua", 246 | NIGER: "niger", 247 | NIGERIA: "nigeria", 248 | NORTH_KOREA: "north-korea", 249 | NORTH_MACEDONIA: "north-macedonia", 250 | NORTHERN_MARIANA_ISLANDS: "northern-mariana-islands", 251 | NORWAY: "norway", 252 | OMAN: "oman", 253 | PAKISTAN: "pakistan", 254 | PALAU: "palau", 255 | PANAMA: "panama", 256 | PAPUA_NEW_GUINEA: "papua-new-guinea", 257 | PARAGUAY: "paraguay", 258 | PERU: "peru", 259 | PHILIPPINES: "philippines", 260 | POLAND: "poland", 261 | PORTUGAL: "portugal", 262 | PUERTO_RICO: "puerto-rico", 263 | QATAR: "qatar", 264 | REUNION: "reunion", 265 | ROMANIA: "romania", 266 | RUSSIA: "russia", 267 | RWANDA: "rwanda", 268 | SAINT_HELENA: "saint-helena", 269 | SAINT_KITTS_AND_NEVIS: "saint-kitts-and-nevis", 270 | SAINT_LUCIA: "saint-lucia", 271 | SAINT_PIERRE_AND_MIQUELON: "saint-pierre-and-miquelon", 272 | SAINT_VINCENT_AND_THE_GRENADINES: "saint-vincent-and-the-grenadines", 273 | SAMOA: "samoa", 274 | SAO_TOME_AND_PRINCIPE: "sao-tome-and-principe", 275 | SAUDI_ARABIA: "saudi-arabia", 276 | SENEGAL: "senegal", 277 | SERBIA: "serbia", 278 | SEYCHELLES: "seychelles", 279 | SIERRA_LEONE: "sierra-leone", 280 | SINGAPORE: "singapore", 281 | SLOVAKIA: "slovakia", 282 | SLOVENIA: "slovenia", 283 | SOLOMON_ISLANDS: "solomon-islands", 284 | SOMALIA: "somalia", 285 | SOUTH_AFRICA: "south-africa", 286 | SOUTH_KOREA: "south-korea", 287 | SOUTH_SUDAN: "south-sudan", 288 | SPAIN: "spain", 289 | SRI_LANKA: "sri-lanka", 290 | SUDAN: "sudan", 291 | SURINAME: "suriname", 292 | SWEDEN: "sweden", 293 | SWITZERLAND: "switzerland", 294 | SYRIA: "syria", 295 | TAIWAN: "taiwan", 296 | TAJIKISTAN: "tajikistan", 297 | TANZANIA: "tanzania", 298 | THAILAND: "thailand", 299 | TIMOR_LESTE_EAST_TIMOR: "timor-leste-east-timor", 300 | TOGO: "togo", 301 | TONGA: "tonga", 302 | TRINIDAD_AND_TOBAGO: "trinidad-and-tobago", 303 | TUNISIA: "tunisia", 304 | TURKEY: "turkey", 305 | TURKMENISTAN: "turkmenistan", 306 | TURKS_AND_CAICOS_ISLANDS: "turks-and-caicos-islands", 307 | TUVALU: "tuvalu", 308 | UGANDA: "uganda", 309 | UKRAINE: "ukraine", 310 | UNITED_ARAB_EMIRATES: "united-arab-emirates", 311 | UNITED_KINGDOM: "united-kingdom", 312 | UNITED_STATES: "united-states", 313 | UNITED_STATES_MINOR_OUTLYING_ISLANDS: "united-states-minor-outlying-islands", 314 | URUGUAY: "uruguay", 315 | UZBEKISTAN: "uzbekistan", 316 | VANUATU: "vanuatu", 317 | VENEZUELA: "venezuela", 318 | VIETNAM: "vietnam", 319 | VIRGIN_ISLANDS_BRITISH: "virgin-islands-british", 320 | VIRGIN_ISLANDS_US: "virgin-islands-us", 321 | WALLIS_AND_FUTUNA: "wallis-and-futuna", 322 | YEMEN: "yemen", 323 | ZAMBIA: "zambia", 324 | ZIMBABWE: "zimbabwe" 325 | }; 326 | 327 | module.exports = new Core(); 328 | module.exports.Countries = Countries; 329 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for FlightRadar24 API 2 | 3 | /** 4 | * Zone boundary coordinates 5 | */ 6 | export interface Zone { 7 | tl_y: number; 8 | br_y: number; 9 | tl_x: number; 10 | br_x: number; 11 | } 12 | 13 | /** 14 | * Main class of the FlightRadarAPI 15 | */ 16 | export class FlightRadar24API { 17 | private __flightTrackerConfig: FlightTrackerConfig; 18 | private __loginData: {userData: any; cookies: any;} | null; 19 | 20 | /** 21 | * Constructor of FlightRadar24API class 22 | * 23 | * @param {string} [user] - Your email (optional) 24 | * @param {string} [password] - Your password (optional) 25 | * @param {number} [timeout=10] - Request timeout in seconds 26 | */ 27 | constructor(user?: string, password?: string, timeout?: number); 28 | 29 | /** 30 | * Return a list with all airlines. 31 | */ 32 | getAirlines(): Promise; 33 | 34 | /** 35 | * Download the logo of an airline from FlightRadar24 and return it as bytes. 36 | * 37 | * @param {string} iata - IATA of the airline 38 | * @param {string} icao - ICAO of the airline 39 | */ 40 | getAirlineLogo( 41 | iata: string, 42 | icao: string, 43 | ): Promise<[object, string] | undefined>; 44 | 45 | /** 46 | * Return basic information about a specific airport. 47 | * 48 | * @param {string} code - ICAO or IATA of the airport 49 | * @param {boolean} details - If true, it returns flights with detailed information 50 | */ 51 | getAirport(code: string, details?: boolean): Promise; 52 | 53 | /** 54 | * Return the airport details from FlightRadar24. 55 | * 56 | * @param {string} code - ICAO or IATA of the airport 57 | * @param {number} [flightLimit=100] - Limit of flights related to the airport 58 | * @param {number} [page=1] - Page of result to display 59 | */ 60 | getAirportDetails( 61 | code: string, 62 | flightLimit?: number, 63 | page?: number, 64 | ): Promise; 65 | 66 | /** 67 | * Return airport disruptions. 68 | */ 69 | getAirportDisruptions(): Promise; 70 | 71 | /** 72 | * Return a list with all airports for specified countries. 73 | * 74 | * @param {string[]} countries - Array of country names 75 | */ 76 | getAirports(countries: string[]): Promise; 77 | 78 | /** 79 | * Return the bookmarks from the FlightRadar24 account. 80 | */ 81 | getBookmarks(): Promise; 82 | 83 | /** 84 | * Convert coordinate dictionary to a string "y1, y2, x1, x2". 85 | * 86 | * @param {Zone} zone - Dictionary containing the following keys: tl_y, tl_x, br_y, br_x 87 | */ 88 | getBounds(zone: Zone): string; 89 | 90 | /** 91 | * Convert a point coordinate and a radius to a string "y1, y2, x1, x2". 92 | * 93 | * @param {number} latitude - Latitude of the point 94 | * @param {number} longitude - Longitude of the point 95 | * @param {number} radius - Radius in meters to create area around the point 96 | */ 97 | getBoundsByPoint( 98 | latitude: number, 99 | longitude: number, 100 | radius: number, 101 | ): string; 102 | 103 | /** 104 | * Download the flag of a country from FlightRadar24 and return it as bytes. 105 | * 106 | * @param {string} country - Country name 107 | */ 108 | getCountryFlag(country: string): Promise<[object, string] | undefined>; 109 | 110 | /** 111 | * Return the flight details from Data Live FlightRadar24. 112 | * 113 | * @param {Flight} flight - A Flight instance 114 | */ 115 | getFlightDetails(flight: Flight): Promise; 116 | 117 | /** 118 | * Return a list of flights. See more options at setFlightTrackerConfig() method. 119 | * 120 | * @param {string} [airline] - The airline ICAO. Ex: "DAL" 121 | * @param {string} [bounds] - Coordinates (y1, y2 ,x1, x2). Ex: "75.78,-75.78,-427.56,427.56" 122 | * @param {string} [registration] - Aircraft registration 123 | * @param {string} [aircraftType] - Aircraft model code. Ex: "B737" 124 | * @param {boolean} [details] - If true, it returns flights with detailed information 125 | */ 126 | getFlights( 127 | airline?: string | null, 128 | bounds?: string | null, 129 | registration?: string | null, 130 | aircraftType?: string | null, 131 | details?: boolean, 132 | ): Promise; 133 | 134 | /** 135 | * Return a copy of the current config of the Real Time Flight Tracker, used by getFlights() method. 136 | */ 137 | getFlightTrackerConfig(): FlightTrackerConfig; 138 | 139 | /** 140 | * Download historical data of a flight. 141 | * 142 | * @param {Flight} flight - A Flight instance. 143 | * @param {string} fileType - Must be "CSV" or "KML" 144 | * @param {number} timestamp - A Unix timestamp 145 | */ 146 | getHistoryData( 147 | flight: Flight, 148 | fileType: "CSV" | "KML", 149 | timestamp: number, 150 | ): Promise; 151 | 152 | /** 153 | * Return the user data. 154 | */ 155 | getLoginData(): object; 156 | 157 | /** 158 | * Return the most tracked data. 159 | */ 160 | getMostTracked(): Promise; 161 | 162 | /** 163 | * Return boundaries of volcanic eruptions and ash clouds impacting aviation. 164 | */ 165 | getVolcanicEruptions(): Promise; 166 | 167 | /** 168 | * Return all major zones on the globe. 169 | */ 170 | getZones(): Promise; 171 | 172 | /** 173 | * Return the search result. 174 | * 175 | * @param {string} query 176 | * @param {number} [limit=50] 177 | */ 178 | search(query: string, limit?: number): Promise; 179 | 180 | /** 181 | * Check if the user is logged into the FlightRadar24 account. 182 | */ 183 | isLoggedIn(): boolean; 184 | 185 | /** 186 | * Log in to a FlightRadar24 account. 187 | * 188 | * @param {string} user - Your email. 189 | * @param {string} password - Your password. 190 | */ 191 | login(user: string, password: string): Promise; 192 | 193 | /** 194 | * Log out of the FlightRadar24 account. 195 | */ 196 | logout(): Promise; 197 | 198 | /** 199 | * Set config for the Real Time Flight Tracker, used by getFlights() method. 200 | * 201 | * @param {FlightTrackerConfig} [flightTrackerConfig] - If null, set to the default config. 202 | * @param {object} [config={}] - Config as an JSON object 203 | */ 204 | setFlightTrackerConfig( 205 | flightTrackerConfig: FlightTrackerConfig | null, 206 | config?: object, 207 | ): Promise; 208 | } 209 | 210 | /** 211 | * Data class with settings of the Real Time Flight Tracker. 212 | */ 213 | export class FlightTrackerConfig { 214 | faa: string; 215 | satellite: string; 216 | mlat: string; 217 | flarm: string; 218 | adsb: string; 219 | gnd: string; 220 | air: string; 221 | vehicles: string; 222 | estimated: string; 223 | maxage: string; 224 | gliders: string; 225 | stats: string; 226 | limit: string; 227 | 228 | /** 229 | * Constructor of FlightTrackerConfig class. 230 | */ 231 | constructor(data: object); 232 | } 233 | 234 | /** 235 | * Representation of a real entity, at some location. 236 | */ 237 | export class Entity { 238 | latitude: number | null; 239 | longitude: number | null; 240 | 241 | /** 242 | * Constructor of Entity class. 243 | * 244 | * @param {number} latitude 245 | * @param {number} longitude 246 | */ 247 | constructor(latitude?: number | null, longitude?: number | null); 248 | 249 | private __setPosition( 250 | latitude: number | null, 251 | longitude: number | null, 252 | ): void; 253 | 254 | private __getInfo(info: any, replaceBy?: any): any; 255 | 256 | /** 257 | * Return the distance from another entity (in kilometers). 258 | * 259 | * @param {Entity} entity 260 | * @return {number} 261 | */ 262 | getDistanceFrom(entity: Entity): number; 263 | } 264 | 265 | /** 266 | * Airport representation. 267 | */ 268 | export class Airport extends Entity { 269 | latitude: number; 270 | longitude: number; 271 | altitude: number | null; 272 | name: string; 273 | icao: string; 274 | iata: string; 275 | country: string; 276 | 277 | /** 278 | * Constructor of Airport class. 279 | * 280 | * The parameters below are optional. You can just create an Airport instance with no information 281 | * and use the setAirportDetails(...) method for having an instance with detailed information. 282 | * 283 | * @param {object} [basicInfo] - Basic information about the airport received from FlightRadar24 284 | * @param {object} [info] - Dictionary with more information about the airport received from FlightRadar24 285 | */ 286 | constructor(basicInfo?: object, info?: object); 287 | 288 | /** 289 | * Initialize instance with basic information about the airport. 290 | * 291 | * @param {object} basicInfo 292 | */ 293 | private __initializeWithBasicInfo(basicInfo: object): void; 294 | 295 | /** 296 | * Initialize instance with extra information about the airport. 297 | * 298 | * @param {object} info 299 | */ 300 | private __initializeWithInfo(info: object): void; 301 | 302 | /** 303 | * Set airport details to the instance. Use FlightRadar24API.getAirportDetails(...) method to get it. 304 | * 305 | * @param {object} airportDetails 306 | */ 307 | setAirportDetails(airportDetails: object): void; 308 | } 309 | 310 | /** 311 | * Flight representation. 312 | */ 313 | export class Flight extends Entity { 314 | latitude: number; 315 | longitude: number; 316 | altitude: number; 317 | id: string; 318 | aircraftCode: string; 319 | airlineIcao: string; 320 | airlineIata: string; 321 | callsign: string; 322 | destinationAirportIata: string; 323 | groundSpeed: number; 324 | heading: number; 325 | number: string; 326 | icao24bit: string; 327 | squawk: string; 328 | registration: string; 329 | time: number; 330 | originAirportIata: string; 331 | onGround: number; 332 | verticalSpeed: number; 333 | 334 | /** 335 | * Constructor of Flight class. 336 | * 337 | * @param {*} flightId - The flight ID specifically used by FlightRadar24 338 | * @param {*} info - Dictionary with received data from FlightRadar24 339 | */ 340 | constructor(flightId: string, info: object); 341 | 342 | /** 343 | * Check one or more flight information. 344 | * 345 | * You can use the prefix "max" or "min" in the parameter 346 | * to compare numeric data with ">" or "<". 347 | * 348 | * Example: checkInfo({minAltitude: 6700, maxAltitude: 13000, airlineIcao: "THY"}) 349 | * 350 | * @param {object} info 351 | */ 352 | checkInfo(info: object): boolean; 353 | 354 | /** 355 | * Return the formatted altitude, with the unit of measure. 356 | */ 357 | getAltitude(): string; 358 | 359 | /** 360 | * Return the formatted flight level, with the unit of measure. 361 | */ 362 | getFlightLevel(): string; 363 | 364 | /** 365 | * Return the formatted ground speed, with the unit of measure. 366 | */ 367 | getGroundSpeed(): string; 368 | 369 | /** 370 | * Return the formatted heading, with the unit of measure. 371 | */ 372 | getHeading(): string; 373 | 374 | /** 375 | * Return the formatted vertical speed, with the unit of measure. 376 | */ 377 | getVerticalSpeed(): string; 378 | 379 | /** 380 | * Set flight details to the instance. Use FlightRadar24API.getFlightDetails(...) method to get it. 381 | * 382 | * @param {object} flightDetails 383 | */ 384 | setFlightDetails(flightDetails: object): void; 385 | } 386 | 387 | export class AirportNotFoundError extends Error { 388 | constructor(message?: string); 389 | } 390 | 391 | export class CloudflareError extends Error { 392 | constructor(message?: string); 393 | } 394 | 395 | export class LoginError extends Error { 396 | constructor(message?: string); 397 | } 398 | 399 | export const author: string; 400 | export const version: string; -------------------------------------------------------------------------------- /python/FlightRadar24/api.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Any, Dict, List, Optional, Tuple, Union 4 | from bs4 import BeautifulSoup 5 | 6 | import dataclasses 7 | import math 8 | 9 | from .core import Core, Countries 10 | from .entities.airport import Airport 11 | from .entities.flight import Flight 12 | from .errors import AirportNotFoundError, LoginError 13 | from .request import APIRequest 14 | 15 | 16 | @dataclasses.dataclass 17 | class FlightTrackerConfig(object): 18 | """ 19 | Data class with settings of the Real Time Flight Tracker. 20 | """ 21 | faa: str = "1" 22 | satellite: str = "1" 23 | mlat: str = "1" 24 | flarm: str = "1" 25 | adsb: str = "1" 26 | gnd: str = "1" 27 | air: str = "1" 28 | vehicles: str = "1" 29 | estimated: str = "1" 30 | maxage: str = "14400" 31 | gliders: str = "1" 32 | stats: str = "1" 33 | limit: str = "5000" 34 | 35 | 36 | class FlightRadar24API(object): 37 | """ 38 | Main class of the FlightRadarAPI 39 | """ 40 | 41 | def __init__(self, user: Optional[str] = None, password: Optional[str] = None, timeout: int = 10): 42 | """ 43 | Constructor of the FlightRadar24API class. 44 | 45 | :param user: Your email (optional) 46 | :param password: Your password (optional) 47 | """ 48 | self.__flight_tracker_config = FlightTrackerConfig() 49 | self.__login_data: Optional[Dict] = None 50 | 51 | self.timeout: int = timeout 52 | 53 | if user is not None and password is not None: 54 | self.login(user, password) 55 | 56 | def get_airlines(self) -> List[Dict]: 57 | """ 58 | Return a list with all airlines. 59 | """ 60 | response = APIRequest(Core.airlines_data_url, headers=Core.html_headers, timeout=self.timeout) 61 | html_content: bytes = response.get_content() 62 | airlines_data = [] 63 | 64 | # Parse HTML content. 65 | soup = BeautifulSoup(html_content, "html.parser") 66 | 67 | tbody = soup.find("tbody") 68 | 69 | if not tbody: 70 | return [] 71 | 72 | # Extract data from HTML content. 73 | tr_elements = tbody.find_all("tr") 74 | 75 | for tr in tr_elements: 76 | td_notranslate = tr.find("td", class_="notranslate") 77 | 78 | if td_notranslate: 79 | a_element = td_notranslate.find("a", href=lambda href: href and href.startswith("/data/airlines")) 80 | 81 | if a_element: 82 | td_elements = tr.find_all("td") 83 | 84 | # Extract airline name. 85 | airline_name = a_element.get_text(strip=True) 86 | 87 | if len(airline_name) < 2: 88 | continue 89 | 90 | # Extract IATA / ICAO codes. 91 | iata = None 92 | icao = None 93 | 94 | if len(td_elements) >= 4: 95 | codes_text = td_elements[3].get_text(strip=True) 96 | 97 | if " / " in codes_text: 98 | parts = codes_text.split(" / ") 99 | 100 | if len(parts) == 2: 101 | iata = parts[0].strip() 102 | icao = parts[1].strip() 103 | 104 | elif len(codes_text) == 2: 105 | iata = codes_text 106 | 107 | elif len(codes_text) == 3: 108 | icao = codes_text 109 | 110 | # Extract number of aircrafts. 111 | n_aircrafts = None 112 | 113 | if len(td_elements) >= 5: 114 | aircrafts_text = td_elements[4].get_text(strip=True) 115 | 116 | if aircrafts_text: 117 | n_aircrafts = aircrafts_text.split(" ", maxsplit=1)[0].strip() 118 | n_aircrafts = int(n_aircrafts) 119 | 120 | airline_data = { 121 | "Name": airline_name, 122 | "ICAO": icao, 123 | "IATA": iata, 124 | "n_aircrafts": n_aircrafts 125 | } 126 | 127 | airlines_data.append(airline_data) 128 | 129 | return airlines_data 130 | 131 | def get_airline_logo(self, iata: str, icao: str) -> Optional[Tuple[bytes, str]]: 132 | """ 133 | Download the logo of an airline from FlightRadar24 and return it as bytes. 134 | """ 135 | iata, icao = iata.upper(), icao.upper() 136 | 137 | first_logo_url = Core.airline_logo_url.format(iata, icao) 138 | 139 | # Try to get the image by the first URL option. 140 | response = APIRequest(first_logo_url, headers=Core.image_headers, exclude_status_codes=[403,], timeout=self.timeout) 141 | status_code = response.get_status_code() 142 | 143 | if not str(status_code).startswith("4"): 144 | return response.get_content(), first_logo_url.split(".")[-1] 145 | 146 | # Get the image by the second airline logo URL. 147 | second_logo_url = Core.alternative_airline_logo_url.format(icao) 148 | 149 | response = APIRequest(second_logo_url, headers=Core.image_headers, timeout=self.timeout) 150 | status_code = response.get_status_code() 151 | 152 | if not str(status_code).startswith("4"): 153 | return response.get_content(), second_logo_url.split(".")[-1] 154 | 155 | def get_airport(self, code: str, *, details: bool = False) -> Airport: 156 | """ 157 | Return basic information about a specific airport. 158 | 159 | :param code: ICAO or IATA of the airport 160 | :param details: If True, it returns an Airport instance with detailed information. 161 | """ 162 | if 4 < len(code) or len(code) < 3: 163 | raise ValueError(f"The code '{code}' is invalid. It must be the IATA or ICAO of the airport.") 164 | 165 | if details: 166 | airport = Airport() 167 | 168 | airport_details = self.get_airport_details(code) 169 | airport.set_airport_details(airport_details) 170 | 171 | return airport 172 | 173 | response = APIRequest(Core.airport_data_url.format(code), headers=Core.json_headers, timeout=self.timeout) 174 | content = response.get_content() 175 | 176 | if not content or not isinstance(content, dict) or not content.get("details"): 177 | raise AirportNotFoundError(f"Could not find an airport by the code '{code}'.") 178 | 179 | return Airport(info=content["details"]) 180 | 181 | def get_airport_details(self, code: str, flight_limit: int = 100, page: int = 1) -> Dict: 182 | """ 183 | Return the airport details from FlightRadar24. 184 | 185 | :param code: ICAO or IATA of the airport 186 | :param flight_limit: Limit of flights related to the airport 187 | :param page: Page of result to display 188 | """ 189 | if 4 < len(code) or len(code) < 3: 190 | raise ValueError(f"The code '{code}' is invalid. It must be the IATA or ICAO of the airport.") 191 | 192 | request_params = {"format": "json"} 193 | 194 | if self.__login_data is not None: 195 | request_params["token"] = self.__login_data["cookies"]["_frPl"] 196 | 197 | # Insert the method parameters into the dictionary for the request. 198 | request_params["code"] = code 199 | request_params["limit"] = flight_limit 200 | request_params["page"] = page 201 | 202 | # Request details from the FlightRadar24. 203 | response = APIRequest(Core.api_airport_data_url, request_params, Core.json_headers, exclude_status_codes=[400,], timeout=self.timeout) 204 | content: Dict = response.get_content() 205 | 206 | if response.get_status_code() == 400 and content.get("errors"): 207 | errors = content["errors"]["errors"]["parameters"] 208 | 209 | if errors.get("limit"): 210 | raise ValueError(errors["limit"]["notBetween"]) 211 | 212 | raise AirportNotFoundError(f"Could not find an airport by the code '{code}'.", errors) 213 | 214 | result = content["result"]["response"] 215 | 216 | # Check whether it received data of an airport. 217 | data = result.get("airport", dict()).get("pluginData", dict()) 218 | 219 | if "details" not in data and len(data.get("runways", [])) == 0 and len(data) <= 3: 220 | raise AirportNotFoundError(f"Could not find an airport by the code '{code}'.") 221 | 222 | # Return the airport details. 223 | return result 224 | 225 | def get_airport_disruptions(self) -> Dict: 226 | """ 227 | Return airport disruptions. 228 | """ 229 | response = APIRequest(Core.airport_disruptions_url, headers=Core.json_headers, timeout=self.timeout) 230 | return response.get_content() 231 | 232 | def get_airports(self, countries: List[Countries]) -> List[Airport]: 233 | """ 234 | Return a list with all airports for specified countries. 235 | 236 | :param countries: List of country names from Countries enum. 237 | """ 238 | airports = [] 239 | 240 | for country_name in countries: 241 | country_href = Core.airports_data_url + "/" + country_name.value 242 | 243 | response = APIRequest(country_href, headers=Core.html_headers, timeout=self.timeout) 244 | 245 | html_content: bytes = response.get_content() 246 | 247 | soup = BeautifulSoup(html_content, "html.parser") 248 | 249 | tbody = soup.find("tbody") 250 | 251 | if not tbody: 252 | continue 253 | 254 | # Extract country name from the URL 255 | country_name = country_href.split("/")[-1].replace("-", " ").title() 256 | 257 | tr_elements = tbody.find_all("tr") 258 | 259 | for tr in tr_elements: 260 | a_elements = tr.find_all("a", attrs={"data-iata": True, "data-lat": True, "data-lon": True}) 261 | 262 | if a_elements: 263 | a_element = a_elements[0] 264 | 265 | icao = "" 266 | iata = a_element.get("data-iata", "").strip() 267 | latitude = a_element.get("data-lat", "").strip() 268 | longitude = a_element.get("data-lon", "").strip() 269 | 270 | airport_text = a_element.get_text(strip=True) 271 | name_part = airport_text 272 | 273 | # Get IATA / ICAO from airport text. 274 | small_element = a_element.find("small") 275 | 276 | if small_element: 277 | codes_text = small_element.get_text(strip=True) 278 | codes_text = codes_text.lstrip("(") 279 | codes_text = codes_text.rstrip(")") 280 | codes_text = codes_text.strip() 281 | 282 | # Remove IATA / ICAO from name part. 283 | name_part = name_part.replace(codes_text, "") 284 | name_part = name_part.replace("()", "").strip() 285 | 286 | # Parse codes (can be "IATA/ICAO", "IATA", or "ICAO") 287 | if "/" in codes_text: 288 | codes = codes_text.split("/") 289 | 290 | code1 = codes[0].strip() 291 | code2 = codes[1].strip() 292 | 293 | iata = code1 if len(code1) == 3 else code2 294 | icao = code1 if len(code1) == 4 else code2 295 | 296 | elif len(codes_text) == 3: 297 | iata = codes_text 298 | 299 | elif len(codes_text) == 4: 300 | icao = codes_text 301 | 302 | # Convert latitude and longitude to float 303 | try: 304 | lat_float = float(latitude) if latitude else 0.0 305 | lon_float = float(longitude) if longitude else 0.0 306 | 307 | except ValueError: 308 | lat_float = 0.0 309 | lon_float = 0.0 310 | 311 | # Create Airport instance with basic_info format 312 | airport_data = { 313 | "name": name_part, 314 | "icao": icao, 315 | "iata": iata, 316 | "lat": lat_float, 317 | "lon": lon_float, 318 | "alt": None, # Altitude not available in this format 319 | "country": country_name 320 | } 321 | 322 | airport = Airport(basic_info=airport_data) 323 | airports.append(airport) 324 | 325 | return airports 326 | 327 | 328 | def get_bookmarks(self) -> Dict: 329 | """ 330 | Get the bookmarks from the FlightRadar24 account. 331 | """ 332 | if not self.is_logged_in(): 333 | raise LoginError("You must log in to your account.") 334 | 335 | headers = Core.json_headers.copy() 336 | headers["accesstoken"] = self.get_login_data()["accessToken"] 337 | 338 | cookies = self.__login_data["cookies"] 339 | 340 | response = APIRequest(Core.bookmarks_url, headers=headers, cookies=cookies, timeout=self.timeout) 341 | return response.get_content() 342 | 343 | def get_bounds(self, zone: Dict[str, float]) -> str: 344 | """ 345 | Convert coordinate dictionary to a string "y1, y2, x1, x2". 346 | 347 | :param zone: Dictionary containing the following keys: tl_y, tl_x, br_y, br_x 348 | """ 349 | return "{},{},{},{}".format(zone["tl_y"], zone["br_y"], zone["tl_x"], zone["br_x"]) 350 | 351 | def get_bounds_by_point(self, latitude: float, longitude: float, radius: float) -> str: 352 | """ 353 | Convert a point coordinate and a radius to a string "y1, y2, x1, x2". 354 | 355 | :param latitude: Latitude of the point 356 | :param longitude: Longitude of the point 357 | :param radius: Radius in meters to create area around the point 358 | """ 359 | half_side_in_km = abs(radius) / 1000 360 | 361 | lat = math.radians(latitude) 362 | lon = math.radians(longitude) 363 | 364 | approx_earth_radius = 6371 365 | hypotenuse_distance = math.sqrt(2 * (math.pow(half_side_in_km, 2))) 366 | 367 | lat_min = math.asin( 368 | math.sin(lat) * math.cos(hypotenuse_distance / approx_earth_radius) 369 | + math.cos(lat) 370 | * math.sin(hypotenuse_distance / approx_earth_radius) 371 | * math.cos(225 * (math.pi / 180)), 372 | ) 373 | lon_min = lon + math.atan2( 374 | math.sin(225 * (math.pi / 180)) 375 | * math.sin(hypotenuse_distance / approx_earth_radius) 376 | * math.cos(lat), 377 | math.cos(hypotenuse_distance / approx_earth_radius) 378 | - math.sin(lat) * math.sin(lat_min), 379 | ) 380 | 381 | lat_max = math.asin( 382 | math.sin(lat) * math.cos(hypotenuse_distance / approx_earth_radius) 383 | + math.cos(lat) 384 | * math.sin(hypotenuse_distance / approx_earth_radius) 385 | * math.cos(45 * (math.pi / 180)), 386 | ) 387 | lon_max = lon + math.atan2( 388 | math.sin(45 * (math.pi / 180)) 389 | * math.sin(hypotenuse_distance / approx_earth_radius) 390 | * math.cos(lat), 391 | math.cos(hypotenuse_distance / approx_earth_radius) 392 | - math.sin(lat) * math.sin(lat_max), 393 | ) 394 | 395 | rad2deg = math.degrees 396 | 397 | zone = { 398 | "tl_y": rad2deg(lat_max), 399 | "br_y": rad2deg(lat_min), 400 | "tl_x": rad2deg(lon_min), 401 | "br_x": rad2deg(lon_max) 402 | } 403 | return self.get_bounds(zone) 404 | 405 | def get_country_flag(self, country: str) -> Optional[Tuple[bytes, str]]: 406 | """ 407 | Download the flag of a country from FlightRadar24 and return it as bytes. 408 | 409 | :param country: Country name 410 | """ 411 | flag_url = Core.country_flag_url.format(country.lower().replace(" ", "-")) 412 | headers = Core.image_headers.copy() 413 | 414 | if "origin" in headers: 415 | headers.pop("origin") # Does not work for this request. 416 | 417 | response = APIRequest(flag_url, headers=headers, timeout=self.timeout) 418 | status_code = response.get_status_code() 419 | 420 | if not str(status_code).startswith("4"): 421 | return response.get_content(), flag_url.split(".")[-1] 422 | 423 | def get_flight_details(self, flight: Flight) -> Dict[Any, Any]: 424 | """ 425 | Return the flight details from Data Live FlightRadar24. 426 | 427 | :param flight: A Flight instance 428 | """ 429 | response = APIRequest(Core.flight_data_url.format(flight.id), headers=Core.json_headers, timeout=self.timeout) 430 | return response.get_content() 431 | 432 | def get_flights( 433 | self, 434 | airline: Optional[str] = None, 435 | bounds: Optional[str] = None, 436 | registration: Optional[str] = None, 437 | aircraft_type: Optional[str] = None, 438 | *, 439 | details: bool = False 440 | ) -> List[Flight]: 441 | """ 442 | Return a list of flights. See more options at set_flight_tracker_config() method. 443 | 444 | :param airline: The airline ICAO. Ex: "DAL" 445 | :param bounds: Coordinates (y1, y2 ,x1, x2). Ex: "75.78,-75.78,-427.56,427.56" 446 | :param registration: Aircraft registration 447 | :param aircraft_type: Aircraft model code. Ex: "B737" 448 | :param details: If True, it returns flights with detailed information 449 | """ 450 | request_params = dataclasses.asdict(self.__flight_tracker_config) 451 | 452 | if self.__login_data is not None: 453 | request_params["enc"] = self.__login_data["cookies"]["_frPl"] 454 | 455 | # Insert the method parameters into the dictionary for the request. 456 | if airline: request_params["airline"] = airline 457 | if bounds: request_params["bounds"] = bounds.replace(",", "%2C") 458 | if registration: request_params["reg"] = registration 459 | if aircraft_type: request_params["type"] = aircraft_type 460 | 461 | # Get all flights from Data Live FlightRadar24. 462 | response = APIRequest(Core.real_time_flight_tracker_data_url, request_params, Core.json_headers, timeout=self.timeout) 463 | response = response.get_content() 464 | 465 | flights: List[Flight] = list() 466 | 467 | for flight_id, flight_info in response.items(): 468 | 469 | # Get flights only. 470 | if not flight_id[0].isnumeric(): 471 | continue 472 | 473 | flight = Flight(flight_id, flight_info) 474 | flights.append(flight) 475 | 476 | # Set flight details. 477 | if details: 478 | flight_details = self.get_flight_details(flight) 479 | flight.set_flight_details(flight_details) 480 | 481 | return flights 482 | 483 | def get_flight_tracker_config(self) -> FlightTrackerConfig: 484 | """ 485 | Return a copy of the current config of the Real Time Flight Tracker, used by get_flights() method. 486 | """ 487 | return dataclasses.replace(self.__flight_tracker_config) 488 | 489 | def get_history_data(self, flight: Flight, file_type: str, timestamp: int) -> Dict: 490 | """ 491 | Download historical data of a flight. 492 | 493 | :param flight: A Flight instance 494 | :param file_type: Must be "CSV" or "KML" 495 | :param timestamp: A Unix timestamp 496 | """ 497 | if not self.is_logged_in(): 498 | raise LoginError("You must log in to your account.") 499 | 500 | file_type = file_type.lower() 501 | 502 | if file_type not in ["csv", "kml"]: 503 | raise ValueError(f"File type '{file_type}' is not supported. Only CSV and KML are supported.") 504 | 505 | response = APIRequest( 506 | Core.historical_data_url.format(flight.id, file_type, timestamp), 507 | headers=Core.json_headers, cookies=self.__login_data["cookies"], 508 | timeout=self.timeout 509 | ) 510 | 511 | content = response.get_content() 512 | return str(content.decode("utf-8")) 513 | 514 | def get_login_data(self) -> Dict[Any, Any]: 515 | """ 516 | Return the user data. 517 | """ 518 | if not self.is_logged_in(): 519 | raise LoginError("You must log in to your account.") 520 | 521 | return self.__login_data["userData"].copy() 522 | 523 | def get_most_tracked(self) -> Dict: 524 | """ 525 | Return the most tracked data. 526 | """ 527 | response = APIRequest(Core.most_tracked_url, headers=Core.json_headers, timeout=self.timeout) 528 | return response.get_content() 529 | 530 | def get_volcanic_eruptions(self) -> Dict: 531 | """ 532 | Return boundaries of volcanic eruptions and ash clouds impacting aviation. 533 | """ 534 | response = APIRequest(Core.volcanic_eruption_data_url, headers=Core.json_headers, timeout=self.timeout) 535 | return response.get_content() 536 | 537 | def get_zones(self) -> Dict[str, Dict]: 538 | """ 539 | Return all major zones on the globe. 540 | """ 541 | # [Deprecated Code] 542 | # response = APIRequest(Core.zones_data_url, headers=Core.json_headers, timeout=self.timeout) 543 | # zones = response.get_content() 544 | zones = Core.static_zones 545 | 546 | if "version" in zones: 547 | zones.pop("version") 548 | 549 | return zones 550 | 551 | def search(self, query: str, limit: int = 50) -> Dict: 552 | """ 553 | Return the search result. 554 | """ 555 | response = APIRequest(Core.search_data_url.format(query, limit), headers=Core.json_headers, timeout=self.timeout) 556 | results = response.get_content().get("results", []) 557 | stats = response.get_content().get("stats", {}) 558 | 559 | i = 0 560 | counted_total = 0 561 | data = {} 562 | for name, count in stats.get("count", {}).items(): 563 | data[name] = [] 564 | while i < counted_total + count and i < len(results): 565 | data[name].append(results[i]) 566 | i += 1 567 | counted_total += count 568 | return data 569 | 570 | def is_logged_in(self) -> bool: 571 | """ 572 | Check if the user is logged into the FlightRadar24 account. 573 | """ 574 | return self.__login_data is not None 575 | 576 | def login(self, user: str, password: str) -> None: 577 | """ 578 | Log in to a FlightRadar24 account. 579 | 580 | :param user: Your email. 581 | :param password: Your password. 582 | """ 583 | data = { 584 | "email": user, 585 | "password": password, 586 | "remember": "true", 587 | "type": "web" 588 | } 589 | 590 | response = APIRequest(Core.user_login_url, headers=Core.json_headers, data=data, timeout=self.timeout) 591 | status_code = response.get_status_code() 592 | content = response.get_content() 593 | 594 | if not str(status_code).startswith("2") or not content.get("success"): 595 | if isinstance(content, dict): raise LoginError(content["message"]) 596 | else: raise LoginError("Your email or password is incorrect") 597 | 598 | self.__login_data = { 599 | "userData": content["userData"], 600 | "cookies": response.get_cookies(), 601 | } 602 | 603 | def logout(self) -> bool: 604 | """ 605 | Log out of the FlightRadar24 account. 606 | 607 | Return a boolean indicating that it successfully logged out of the server. 608 | """ 609 | if self.__login_data is None: return True 610 | 611 | cookies = self.__login_data["cookies"] 612 | self.__login_data = None 613 | 614 | response = APIRequest(Core.user_login_url, headers=Core.json_headers, cookies=cookies, timeout=self.timeout) 615 | return str(response.get_status_code()).startswith("2") 616 | 617 | def set_flight_tracker_config( 618 | self, 619 | flight_tracker_config: Optional[FlightTrackerConfig] = None, 620 | **config: Union[int, str] 621 | ) -> None: 622 | """ 623 | Set config for the Real Time Flight Tracker, used by get_flights() method. 624 | """ 625 | if flight_tracker_config is not None: 626 | self.__flight_tracker_config = flight_tracker_config 627 | 628 | current_config_dict = dataclasses.asdict(self.__flight_tracker_config) 629 | 630 | for key, value in config.items(): 631 | value = str(value) 632 | 633 | if key not in current_config_dict: 634 | raise KeyError(f"Unknown option: '{key}'") 635 | 636 | if not value.isdecimal(): 637 | raise TypeError(f"Value must be a decimal. Got '{key}'") 638 | 639 | setattr(self.__flight_tracker_config, key, value) 640 | -------------------------------------------------------------------------------- /nodejs/FlightRadar24/api.js: -------------------------------------------------------------------------------- 1 | const Core = require("./core"); 2 | const APIRequest = require("./request"); 3 | const Airport = require("./entities/airport"); 4 | const Flight = require("./entities/flight"); 5 | const FlightTrackerConfig = require("./flightTrackerConfig"); 6 | const {AirportNotFoundError, LoginError} = require("./errors"); 7 | const {isNumeric} = require("./util"); 8 | const {JSDOM} = require("jsdom"); 9 | 10 | 11 | /** 12 | * Main class of the FlightRadarAPI 13 | */ 14 | class FlightRadar24API { 15 | /** 16 | * Constructor of FlightRadar24API class 17 | */ 18 | constructor() { 19 | this.__flightTrackerConfig = new FlightTrackerConfig(); 20 | this.__loginData = null; 21 | } 22 | 23 | /** 24 | * Return a list with all airlines. 25 | * 26 | * @return {Array} 27 | */ 28 | async getAirlines() { 29 | const response = new APIRequest(Core.airlinesDataUrl, null, Core.htmlHeaders); 30 | await response.receive(); 31 | 32 | const htmlContent = await response.getContent(); 33 | const airlinesData = []; 34 | 35 | // Parse HTML content. 36 | const dom = new JSDOM(htmlContent); 37 | const document = dom.window.document; 38 | 39 | const tbody = document.querySelector("tbody"); 40 | 41 | if (!tbody) { 42 | return []; 43 | } 44 | 45 | // Extract data from HTML content. 46 | const trElements = tbody.querySelectorAll("tr"); 47 | 48 | for (const tr of trElements) { 49 | const tdNotranslate = tr.querySelector("td.notranslate"); 50 | 51 | if (tdNotranslate) { 52 | const aElement = tdNotranslate.querySelector("a[href^='/data/airlines']"); 53 | 54 | if (aElement) { 55 | const tdElements = tr.querySelectorAll("td"); 56 | 57 | // Extract airline name. 58 | const airlineName = aElement.textContent.trim(); 59 | 60 | if (airlineName.length < 2) { 61 | continue; 62 | } 63 | 64 | // Extract IATA / ICAO codes. 65 | let iata = null; 66 | let icao = null; 67 | 68 | if (tdElements.length >= 4) { 69 | const codesText = tdElements[3].textContent.trim(); 70 | 71 | if (codesText.includes(" / ")) { 72 | const parts = codesText.split(" / "); 73 | 74 | if (parts.length === 2) { 75 | iata = parts[0].trim(); 76 | icao = parts[1].trim(); 77 | } 78 | } else if (codesText.length === 2) { 79 | iata = codesText; 80 | } else if (codesText.length === 3) { 81 | icao = codesText; 82 | } 83 | } 84 | 85 | // Extract number of aircrafts. 86 | let nAircrafts = null; 87 | 88 | if (tdElements.length >= 5) { 89 | const aircraftsText = tdElements[4].textContent.trim(); 90 | 91 | if (aircraftsText) { 92 | nAircrafts = aircraftsText.split(" ")[0].trim(); 93 | nAircrafts = parseInt(nAircrafts); 94 | } 95 | } 96 | 97 | const airlineData = { 98 | "Name": airlineName, 99 | "ICAO": icao, 100 | "IATA": iata, 101 | "n_aircrafts": nAircrafts 102 | }; 103 | 104 | airlinesData.push(airlineData); 105 | } 106 | } 107 | } 108 | 109 | return airlinesData; 110 | } 111 | 112 | /** 113 | * Download the logo of an airline from FlightRadar24 and return it as bytes. 114 | * 115 | * @param {string} iata - IATA of the airline 116 | * @param {string} icao - ICAO of the airline 117 | * @return {[object, string]} 118 | */ 119 | async getAirlineLogo(iata, icao) { 120 | iata = iata.toUpperCase(); 121 | icao = icao.toUpperCase(); 122 | 123 | const firstLogoUrl = Core.airlineLogoUrl.format(iata, icao); 124 | 125 | // Try to get the image by the first URL option. 126 | let response = new APIRequest(firstLogoUrl, null, Core.imageHeaders, null, null, [403]); 127 | await response.receive(); 128 | 129 | let statusCode = response.getStatusCode(); 130 | 131 | if (!statusCode.toString().startsWith("4")) { 132 | const splitUrl = firstLogoUrl.split("."); 133 | return [(await response.getContent()), splitUrl[splitUrl.length - 1]]; 134 | } 135 | 136 | // Get the image by the second airline logo URL. 137 | const secondLogoUrl = Core.alternativeAirlineLogoUrl.format(icao); 138 | 139 | response = new APIRequest(secondLogoUrl, null, Core.imageHeaders); 140 | await response.receive(); 141 | 142 | statusCode = response.getStatusCode(); 143 | 144 | if (!statusCode.toString().startsWith("4")) { 145 | const splitUrl = secondLogoUrl.split("."); 146 | return [(await response.getContent()), splitUrl[splitUrl.length - 1]]; 147 | } 148 | } 149 | 150 | /** 151 | * Return basic information about a specific airport. 152 | * 153 | * @param {string} code - ICAO or IATA of the airport 154 | * @param {boolean} details - If true, it returns flights with detailed information 155 | * @return {Airport} 156 | */ 157 | async getAirport(code, details = false) { 158 | if (4 < code.length || code.length < 3) { 159 | throw new Error("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport."); 160 | } 161 | 162 | if (details) { 163 | const airport = new Airport(); 164 | 165 | const airportDetails = await this.getAirportDetails(code); 166 | airport.setAirportDetails(airportDetails); 167 | 168 | return airport; 169 | } 170 | 171 | const response = new APIRequest(Core.airportDataUrl.format(code), null, Core.jsonHeaders); 172 | await response.receive(); 173 | 174 | const info = (await response.getContent())["details"]; 175 | 176 | if (info === undefined) { 177 | throw new AirportNotFoundError("Could not find an airport by the code '" + code + "'."); 178 | } 179 | return new Airport({}, info); 180 | } 181 | 182 | /** 183 | * Return the airport details from FlightRadar24. 184 | * 185 | * @param {string} code - ICAO or IATA of the airport 186 | * @param {number} [flightLimit=100] - Limit of flights related to the airport 187 | * @param {number} [page=1] - Page of result to display 188 | * @return {object} 189 | */ 190 | async getAirportDetails(code, flightLimit = 100, page = 1) { 191 | if (4 < code.length || code.length < 3) { 192 | throw new Error("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport."); 193 | } 194 | 195 | const requestParams = {"format": "json"}; 196 | 197 | if (this.__loginData != null) { 198 | requestParams["token"] = this.__loginData["cookies"]["_frPl"]; 199 | } 200 | 201 | // Insert the method parameters into the dictionary for the request. 202 | requestParams["code"] = code; 203 | requestParams["limit"] = flightLimit; 204 | requestParams["page"] = page; 205 | 206 | // Request details from the FlightRadar24. 207 | const response = new APIRequest(Core.apiAirportDataUrl, requestParams, Core.jsonHeaders, null, null, [400]); 208 | await response.receive(); 209 | 210 | const content = await response.getContent(); 211 | 212 | if (response.getStatusCode() === 400 && content?.["errors"] !== undefined) { 213 | const errors = content["errors"]?.["errors"]?.["parameters"]; 214 | const limit = errors?.["limit"]; 215 | 216 | if (limit !== undefined) { 217 | throw new Error(limit["notBetween"]); 218 | } 219 | throw new AirportNotFoundError("Could not find an airport by the code '" + code + "'.", errors); 220 | } 221 | 222 | const result = content["result"]["response"]; 223 | 224 | // Check whether it received data of an airport. 225 | const data = result?.["airport"]?.["pluginData"]; 226 | const dataCount = typeof data === "object" ? Object.entries(data).length : 0; 227 | 228 | const runways = data?.["runways"]; 229 | const runwaysCount = typeof runways === "object" ? Object.entries(runways).length : 0; 230 | 231 | if (data?.["details"] === undefined && runwaysCount == 0 && dataCount <= 3) { 232 | throw new AirportNotFoundError("Could not find an airport by the code '" + code + "'."); 233 | } 234 | 235 | // Return the airport details. 236 | return result; 237 | } 238 | 239 | /** 240 | * Return airport disruptions. 241 | * 242 | * @return {object} 243 | */ 244 | async getAirportDisruptions() { 245 | const response = new APIRequest(Core.airportDisruptionsUrl, null, Core.jsonHeaders); 246 | await response.receive(); 247 | 248 | return await response.getContent(); 249 | } 250 | 251 | /** 252 | * Return a list with all airports for specified countries. 253 | * 254 | * @param {Array} countries - Array of country names from Countries enum 255 | * @return {Array} 256 | */ 257 | async getAirports(countries) { 258 | const airports = []; 259 | 260 | for (const countryName of countries) { 261 | const countryHref = Core.airportsDataUrl + "/" + countryName; 262 | 263 | const response = new APIRequest(countryHref, null, Core.htmlHeaders); 264 | await response.receive(); 265 | 266 | const htmlContent = await response.getContent(); 267 | 268 | // Parse HTML content. 269 | const dom = new JSDOM(htmlContent); 270 | const document = dom.window.document; 271 | 272 | const tbody = document.querySelector("tbody"); 273 | 274 | if (!tbody) { 275 | continue; 276 | } 277 | 278 | // Extract country name from the URL 279 | const countryDisplayName = countryHref.split("/").pop().replace(/-/g, " ") 280 | .split(" ").map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(" "); 281 | 282 | const trElements = tbody.querySelectorAll("tr"); 283 | 284 | for (const tr of trElements) { 285 | const aElements = tr.querySelectorAll("a[data-iata][data-lat][data-lon]"); 286 | 287 | if (aElements.length > 0) { 288 | const aElement = aElements[0]; 289 | 290 | let icao = ""; 291 | let iata = aElement.getAttribute("data-iata") || ""; 292 | const latitude = aElement.getAttribute("data-lat") || ""; 293 | const longitude = aElement.getAttribute("data-lon") || ""; 294 | 295 | const airportText = aElement.textContent.trim(); 296 | let namePart = airportText; 297 | 298 | // Get IATA / ICAO from airport text. 299 | const smallElement = aElement.querySelector("small"); 300 | 301 | if (smallElement) { 302 | let codesText = smallElement.textContent.trim(); 303 | codesText = codesText.replace(/^\(/, "").replace(/\)$/, "").trim(); 304 | 305 | // Remove IATA / ICAO from name part. 306 | namePart = namePart.replace(smallElement.textContent, "").replace(/\(\)/, "").trim(); 307 | 308 | // Parse codes (can be "IATA/ICAO", "IATA", or "ICAO") 309 | if (codesText.includes("/")) { 310 | const codes = codesText.split("/"); 311 | const code1 = codes[0].trim(); 312 | const code2 = codes[1].trim(); 313 | 314 | // Use length to determine IATA vs ICAO 315 | if (code1.length === 3 && code2.length === 4) { 316 | iata = code1; 317 | icao = code2; 318 | } else if (code1.length === 4 && code2.length === 3) { 319 | iata = code2; 320 | icao = code1; 321 | } 322 | } else if (codesText.length === 3) { 323 | iata = codesText; 324 | } else if (codesText.length === 4) { 325 | icao = codesText; 326 | } 327 | } 328 | 329 | // Convert latitude and longitude to float 330 | let latFloat = 0.0; 331 | let lonFloat = 0.0; 332 | 333 | try { 334 | latFloat = latitude ? parseFloat(latitude) : 0.0; 335 | lonFloat = longitude ? parseFloat(longitude) : 0.0; 336 | } catch (error) { 337 | latFloat = 0.0; 338 | lonFloat = 0.0; 339 | } 340 | 341 | // Create Airport instance with basic_info format 342 | const airportData = { 343 | "name": namePart, 344 | "icao": icao, 345 | "iata": iata, 346 | "lat": latFloat, 347 | "lon": lonFloat, 348 | "alt": null, // Altitude not available in this format 349 | "country": countryDisplayName 350 | }; 351 | 352 | const airport = new Airport(airportData); 353 | airports.push(airport); 354 | } 355 | } 356 | } 357 | 358 | return airports; 359 | } 360 | 361 | /** 362 | * Return the bookmarks from the FlightRadar24 account. 363 | * 364 | * @return {object} 365 | */ 366 | async getBookmarks() { 367 | if (!this.isLoggedIn()) { 368 | throw new LoginError("You must log in to your account."); 369 | } 370 | 371 | const headers = {...Core.jsonHeaders}; 372 | headers["accesstoken"] = this.getLoginData()["accessToken"]; 373 | 374 | const cookies = this.__loginData["cookies"]; 375 | 376 | const response = new APIRequest(Core.bookmarksUrl, null, headers, null, cookies); 377 | await response.receive(); 378 | 379 | return await response.getContent(); 380 | } 381 | 382 | /** 383 | * Convert coordinate dictionary to a string "y1, y2, x1, x2". 384 | * 385 | * @param {object} zone - Dictionary containing the following keys: tl_y, tl_x, br_y, br_x 386 | * @return {string} 387 | */ 388 | getBounds(zone) { 389 | return "" + zone["tl_y"] + "," + zone["br_y"] + "," + zone["tl_x"] + "," + zone["br_x"]; 390 | } 391 | 392 | /** 393 | * Convert a point coordinate and a radius to a string "y1, y2, x1, x2". 394 | * 395 | * @param {number} latitude - Latitude of the point 396 | * @param {number} longitude - Longitude of the point 397 | * @param {number} radius - Radius in meters to create area around the point 398 | * @return {string} 399 | */ 400 | getBoundsByPoint(latitude, longitude, radius) { 401 | const halfSideInKm = Math.abs(radius) / 1000; 402 | 403 | Math.rad2deg = (x) => x * (180 / Math.PI); 404 | Math.radians = (x) => x * (Math.PI / 180); 405 | 406 | const lat = Math.radians(latitude); 407 | const lon = Math.radians(longitude); 408 | 409 | const approxEarthRadius = 6371; 410 | const hypotenuseDistance = Math.sqrt(2 * (Math.pow(halfSideInKm, 2))); 411 | 412 | const latMin = Math.asin( 413 | Math.sin(lat) * Math.cos(hypotenuseDistance / approxEarthRadius) + 414 | Math.cos(lat) * 415 | Math.sin(hypotenuseDistance / approxEarthRadius) * 416 | Math.cos(225 * (Math.PI / 180)), 417 | ); 418 | const lonMin = lon + Math.atan2( 419 | Math.sin(225 * (Math.PI / 180)) * 420 | Math.sin(hypotenuseDistance / approxEarthRadius) * 421 | Math.cos(lat), 422 | Math.cos(hypotenuseDistance / approxEarthRadius) - 423 | Math.sin(lat) * Math.sin(latMin), 424 | ); 425 | 426 | const latMax = Math.asin( 427 | Math.sin(lat) * Math.cos(hypotenuseDistance / approxEarthRadius) + 428 | Math.cos(lat) * 429 | Math.sin(hypotenuseDistance / approxEarthRadius) * 430 | Math.cos(45 * (Math.PI / 180)), 431 | ); 432 | const lonMax = lon + Math.atan2( 433 | Math.sin(45 * (Math.PI / 180)) * 434 | Math.sin(hypotenuseDistance / approxEarthRadius) * 435 | Math.cos(lat), 436 | Math.cos(hypotenuseDistance / approxEarthRadius) - 437 | Math.sin(lat) * Math.sin(latMax), 438 | ); 439 | 440 | const zone = { 441 | "tl_y": Math.rad2deg(latMax), 442 | "br_y": Math.rad2deg(latMin), 443 | "tl_x": Math.rad2deg(lonMin), 444 | "br_x": Math.rad2deg(lonMax), 445 | }; 446 | return this.getBounds(zone); 447 | } 448 | 449 | /** 450 | * Download the flag of a country from FlightRadar24 and return it as bytes. 451 | * 452 | * @param {string} country - Country name 453 | * @return {[object, string]} 454 | */ 455 | async getCountryFlag(country) { 456 | const flagUrl = Core.countryFlagUrl.format(country.toLowerCase().replace(" ", "-")); 457 | const headers = {...Core.imageHeaders}; 458 | 459 | if (headers.hasOwnProperty("origin")) { 460 | delete headers["origin"]; // Does not work for this request. 461 | } 462 | 463 | const response = new APIRequest(flagUrl, null, headers); 464 | await response.receive(); 465 | 466 | const statusCode = response.getStatusCode(); 467 | 468 | if (!statusCode.toString().startsWith("4")) { 469 | const splitUrl = flagUrl.split("."); 470 | return [(await response.getContent()), splitUrl[splitUrl.length - 1]]; 471 | } 472 | } 473 | 474 | /** 475 | * Return the flight details from Data Live FlightRadar24. 476 | * 477 | * @param {Flight} flight - A Flight instance 478 | * @return {object} 479 | */ 480 | async getFlightDetails(flight) { 481 | const response = new APIRequest(Core.flightDataUrl.format(flight.id), null, Core.jsonHeaders); 482 | await response.receive(); 483 | 484 | return (await response.getContent()); 485 | } 486 | 487 | /** 488 | * Return a list of flights. See more options at setFlightTrackerConfig() method. 489 | * 490 | * @param {string} [airline] - The airline ICAO. Ex: "DAL" 491 | * @param {string} [bounds] - Coordinates (y1, y2 ,x1, x2). Ex: "75.78,-75.78,-427.56,427.56" 492 | * @param {string} [registration] - Aircraft registration 493 | * @param {string} [aircraftType] - Aircraft model code. Ex: "B737" 494 | * @param {boolean} [details] - If true, it returns flights with detailed information 495 | * @return {Array} 496 | */ 497 | async getFlights(airline = null, bounds = null, registration = null, aircraftType = null, details = false) { 498 | const requestParams = {...this.__flightTrackerConfig}; 499 | 500 | if (this.__loginData != null) { 501 | requestParams["enc"] = this.__loginData["cookies"]["_frPl"]; 502 | } 503 | 504 | // Insert the method parameters into the dictionary for the request. 505 | if (airline != null) { 506 | requestParams["airline"] = airline; 507 | } 508 | if (bounds != null) { 509 | requestParams["bounds"] = bounds.replace(",", "%2C"); 510 | } 511 | if (registration != null) { 512 | requestParams["reg"] = registration; 513 | } 514 | if (aircraftType != null) { 515 | requestParams["type"] = aircraftType; 516 | } 517 | 518 | // Get all flights from Data Live FlightRadar24. 519 | const response = new APIRequest(Core.realTimeFlightTrackerDataUrl, requestParams, Core.jsonHeaders); 520 | await response.receive(); 521 | 522 | const content = await response.getContent(); 523 | const flights = []; 524 | 525 | for (const flightId in content) { 526 | if (!Object.prototype.hasOwnProperty.call(content, flightId)) { // guard-for-in 527 | continue; 528 | } 529 | 530 | const flightInfo = content[flightId]; 531 | 532 | // Get flights only. 533 | if (!isNumeric(flightId[0])) { 534 | continue; 535 | } 536 | 537 | const flight = new Flight(flightId, flightInfo); 538 | flights.push(flight); 539 | 540 | // Set flight details. 541 | if (details) { 542 | const flightDetails = await this.getFlightDetails(flight); 543 | flight.setFlightDetails(flightDetails); 544 | } 545 | } 546 | 547 | return flights; 548 | } 549 | 550 | /** 551 | * Return a copy of the current config of the Real Time Flight Tracker, used by getFlights() method. 552 | * 553 | * @return {FlightTrackerConfig} 554 | */ 555 | getFlightTrackerConfig() { 556 | return new FlightTrackerConfig({...this.__flightTrackerConfig}); 557 | } 558 | 559 | /** 560 | * Download historical data of a flight. 561 | * 562 | * @param {Flight} flight - A Flight instance. 563 | * @param {string} fileType - Must be "CSV" or "KML" 564 | * @param {number} timestamp - A Unix timestamp 565 | */ 566 | async getHistoryData(flight, fileType, timestamp) { 567 | if (!this.isLoggedIn()) { 568 | throw new LoginError("You must log in to your account."); 569 | } 570 | 571 | fileType = fileType.toLowerCase(); 572 | 573 | if (!["csv", "kml"].includes(fileType)) { 574 | throw new Error("File type '" + fileType + "' is not supported. Only CSV and KML are supported."); 575 | } 576 | 577 | const response = new APIRequest( 578 | Core.historicalDataUrl.format(flight.id, fileType, timestamp), 579 | null, Core.jsonHeaders, null, self.__loginData["cookies"], 580 | ); 581 | await response.receive(); 582 | 583 | const content = await response.getContent(); 584 | return content; 585 | } 586 | 587 | /** 588 | * Return the user data. 589 | * 590 | * @return {object} 591 | */ 592 | getLoginData() { 593 | if (!this.isLoggedIn()) { 594 | throw new LoginError("You must log in to your account."); 595 | } 596 | return {...this.__loginData["userData"]}; 597 | } 598 | 599 | /** 600 | * Return the most tracked data. 601 | * 602 | * @return {object} 603 | */ 604 | async getMostTracked() { 605 | const response = new APIRequest(Core.mostTrackedUrl, null, Core.jsonHeaders); 606 | await response.receive(); 607 | 608 | return await response.getContent(); 609 | } 610 | 611 | /** 612 | * Return boundaries of volcanic eruptions and ash clouds impacting aviation. 613 | * 614 | * @return {object} 615 | */ 616 | async getVolcanicEruptions() { 617 | const response = new APIRequest(Core.volcanicEruptionDataUrl, null, Core.jsonHeaders); 618 | await response.receive(); 619 | 620 | return await response.getContent(); 621 | } 622 | 623 | /** 624 | * Return all major zones on the globe. 625 | * 626 | * @return {object} 627 | */ 628 | async getZones() { 629 | // [Deprecated Code] 630 | // const response = new APIRequest(Core.zonesDataUrl, null, Core.jsonHeaders); 631 | // await response.receive(); 632 | 633 | // const zones = await response.getContent(); 634 | const zones = Core.staticZones; 635 | 636 | if (zones.hasOwnProperty("version")) { 637 | delete zones["version"]; 638 | } 639 | return zones; 640 | } 641 | 642 | /** 643 | * Return the search result. 644 | * 645 | * @param {string} query 646 | * @param {number} [limit=50] 647 | * @return {object} 648 | */ 649 | async search(query, limit = 50) { 650 | const url = Core.searchDataUrl.format(query, limit); 651 | 652 | const response = new APIRequest(url, null, Core.jsonHeaders); 653 | await response.receive(); 654 | 655 | const content = await response.getContent(); 656 | 657 | let results = content["results"]; 658 | results = results == null ? [] : results; 659 | 660 | let stats = content["stats"]; 661 | stats = stats == null ? {} : stats; 662 | 663 | let countDict = stats["count"]; 664 | countDict = countDict == null ? {} : countDict; 665 | 666 | let index = 0; 667 | let countedTotal = 0; 668 | 669 | const data = {}; 670 | 671 | for (const name in countDict) { 672 | if (!Object.prototype.hasOwnProperty.call(countDict, name)) { // guard-for-in 673 | continue; 674 | } 675 | 676 | const count = countDict[name]; 677 | 678 | data[name] = []; 679 | 680 | while (index < (countedTotal + count) && (index < results.length)) { 681 | data[name].push(results[index]); 682 | index++; 683 | } 684 | countedTotal += count; 685 | } 686 | 687 | return data; 688 | } 689 | 690 | /** 691 | * Check if the user is logged into the FlightRadar24 account. 692 | * 693 | * @return {boolean} 694 | */ 695 | isLoggedIn() { 696 | return this.__loginData != null; 697 | } 698 | 699 | /** 700 | * Log in to a FlightRadar24 account. 701 | * 702 | * @param {string} user - Your email. 703 | * @param {string} password - Your password. 704 | * @return {undefined} 705 | */ 706 | async login(user, password) { 707 | const data = { 708 | "email": user, 709 | "password": password, 710 | "remember": "true", 711 | "type": "web", 712 | }; 713 | 714 | const response = new APIRequest(Core.userLoginUrl, null, Core.jsonHeaders, data); 715 | await response.receive(); 716 | 717 | const statusCode = response.getStatusCode(); 718 | const content = await response.getContent(); 719 | 720 | if (!statusCode.toString().startsWith("2") || !content["success"]) { 721 | if (typeof content === "object") { 722 | throw new LoginError(content["message"]); 723 | } 724 | else { 725 | throw new LoginError("Your email or password is incorrect"); 726 | } 727 | } 728 | 729 | this.__loginData = { 730 | "userData": content["userData"], 731 | "cookies": response.getCookies(), 732 | }; 733 | } 734 | 735 | /** 736 | * Log out of the FlightRadar24 account. 737 | * 738 | * @return {boolean} - Return a boolean indicating that it successfully logged out of the server. 739 | */ 740 | async logout() { 741 | if (this.__loginData == null) { 742 | return true; 743 | } 744 | 745 | const cookies = this.__loginData["cookies"]; 746 | this.__loginData = null; 747 | 748 | const response = new APIRequest(Core.userLoginUrl, null, Core.jsonHeaders, null, cookies); 749 | await response.receive(); 750 | 751 | return response.getStatusCode().toString().startsWith("2"); 752 | } 753 | 754 | /** 755 | * Set config for the Real Time Flight Tracker, used by getFlights() method. 756 | * 757 | * @param {FlightTrackerConfig} [flightTrackerConfig] - If null, set to the default config. 758 | * @param {object} [config={}] - Config as an JSON object 759 | * @return {undefined} 760 | */ 761 | async setFlightTrackerConfig(flightTrackerConfig = null, config = {}) { 762 | if (flightTrackerConfig != null) { 763 | this.__flightTrackerConfig = flightTrackerConfig; 764 | } 765 | 766 | for (const key in config) { 767 | if (Object.prototype.hasOwnProperty.call(config, key)) { // guard-for-in 768 | const value = config[key].toString(); 769 | this.__flightTrackerConfig[key] = value; 770 | } 771 | } 772 | } 773 | } 774 | 775 | module.exports = FlightRadar24API; 776 | --------------------------------------------------------------------------------