├── 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 | [](https://github.com/JeanExtreme002/FlightRadarAPI/actions)
9 | [](https://pypi.org/project/FlightRadarAPI/)
10 | [](https://github.com/JeanExtreme002/FlightRadarAPI)
11 | [](https://pypi.org/project/FlightRadarAPI/)
12 | [](https://www.npmjs.com/package/flightradarapi)
13 | [](https://pypi.org/project/FlightRadarAPI/)
14 | [](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 | [](https://github.com/JeanExtreme002/FlightRadarAPI/actions)
9 | [](https://pypi.org/project/FlightRadarAPI/)
10 | [](https://github.com/JeanExtreme002/FlightRadarAPI)
11 | [](https://pypi.org/project/FlightRadarAPI/)
12 | [](https://www.npmjs.com/package/flightradarapi)
13 | [](https://pypi.org/project/FlightRadarAPI/)
14 | [](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 | [](https://github.com/JeanExtreme002/FlightRadarAPI/actions)
10 | [](https://pypi.org/project/FlightRadarAPI/)
11 | [](https://github.com/JeanExtreme002/FlightRadarAPI)
12 | [](https://pypi.org/project/FlightRadarAPI/)
13 | [](https://www.npmjs.com/package/flightradarapi)
14 | [](https://pypi.org/project/FlightRadarAPI/)
15 | [](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 | [](https://github.com/JeanExtreme002/FlightRadarAPI/actions)
9 | [](https://pypi.org/project/FlightRadarAPI/)
10 | [](https://github.com/JeanExtreme002/FlightRadarAPI)
11 | [](https://pypi.org/project/FlightRadarAPI/)
12 | [](https://www.npmjs.com/package/flightradarapi)
13 | [](https://pypi.org/project/FlightRadarAPI/)
14 | [](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 |
--------------------------------------------------------------------------------