Skip to main content

Frago

Frago is a reusable Django app that provides a secure, resumable, and parallel chunked file upload system. It supports large file uploads with resumable support, duplicate chunk detection, checksum verification, and customizable signal hooks — ideal for drone videos, media applications, and IoT devices.


🚀 Features

  • Parallel & resumable chunked uploads
  • Upload tracking via database (start, end, size per chunk)
  • Checksum verification (MD5 or other algorithms)
  • Duplicate chunk detection
  • Upload expiration support
  • Extensible via Django signals
  • Pluggable authentication (user/device/anonymous)
  • Local file storage (pluggable)

📦 Installation

pip install frago

Add frago to your Django INSTALLED_APPS:
    # settings.py
    INSTALLED_APPS = [
        ...
        "frago",
    ]

Finally, run the following commands to create and apply the migrations:

    python manage.py makemigrations frago
    
    python manage.py migrate
    

⚙️ Configuration (optional)

you can confic these in settings.py

#🧠 How the uploader identifies the upload session

CHUNKED_UPLOADER_IDENTIFIER_MODE = "user"
 Options: "anonymous" (default), "user"
 You can override get_identifier() for custom logic.

# 📂 Where uploaded chunks are stored temporarily
CHUNKED_UPLOADER_CHUNK_UPLOAD_PATH = "chunked_uploads/"

# 📂 Where final assembled files go
CHUNKED_UPLOADER_ASSEMBLED_PATH = "assembled_videos/"

# 🔐 Hash type for file integrity checks
CHUNKED_UPLOADER_CHECKSUM_TYPE = "md5"
 Any hashlib-supported algorithm (e.g., "sha256")

# ✅ Whether to perform checksum verification
CHUNKED_UPLOADER_DO_CHECKSUM = True

# ⏱️ Chunk expiration time (used for cleanup jobs)
CHUNKED_UPLOADER_EXPIRATION = timedelta(days=1)

# 📦 Read buffer size during assembly
CHUNKED_UPLOADER_ASSEMBLE_READ_SIZE = 8 * 1024 * 1024  # 8MB

# 🧱 Custom chunk model path (if overriding)
CHUNKED_UPLOADER_CHUNK_MODEL = "frago.ChunkedUploadChunk"

# 🧩 Custom upload model path (if overriding)
CHUNKED_UPLOADER_UPLOAD_MODEL = "frago.ChunkUpload"

🧩 API Usage

1. Start a new upload
        POST /upload/
        Request:
        {
        "filename": "video.mp4",
        "total_size": 104857600
        }

        Response:
        {
        "status": true,
        "upload_id": "uuid4-string",
        "message": "Upload started for video.mp4"
        }
2. Upload a chunk
        PUT /upload/{upload_id}/

        Headers:
            Content-Range: bytes 0-1048575/104857600

        Body: multipart/form-data with key file and binary chunk data.
3. Finalize upload
        POST /upload/{upload_id}/

        Request
        {
        "checksum": "d41d8cd98f00b204e9800998ecf8427e",
        "checksum_algo": "md5"
        }

        Response
        {
        "status": true,
        "upload_id": "uuid4-string",
        "message": "Upload completed for video.mp4"
        }
4. Get upload status (only for authenticated users)
        GET /upload/{upload_id}/

        Or list uploads by current user/device:
        GET /upload/

🧠 Models

ChunkUpload

    Tracks a single upload:

        upload_id: UUID

        filename, total_size

        status: in_progress, complete, expired

        created_at, completed_at

ChunkedUploadChunk

    Tracks individual uploaded chunks:

        start, end, size

        Foreign key to ChunkUpload

🧰 Signals

Frago emits the following Django signals:
Signal	Triggered When
    upload_started : New upload is created
    chunk_received	: A chunk is received and saved
    upload_completed : All chunks assembled, upload finalized
    checksum_failed : Checksum mismatch during finalization

To extend behavior, connect signal handlers:
        from frago.signals import upload_started
        def my_handler(sender, upload, **kwargs):
            print("Upload started:", upload.filename)

        upload_started.connect(my_handler)

🧪 Usage Example

Add to your urls.py (if you are using with out any overides):
        path('api/',include('frago.urls')),

🧼 Cleanup:

Uploads expire after a period (EXPIRATION) and are marked expired.
Chunks and their metadata are deleted automatically on completion.

OVERIDING

This is an example how you can overide and set your custome logic

🔒 Custom Authentication Example

By default, the view allows anonymous access. You should subclass the view and add authentication:

    from rest_framework.permissions import IsAuthenticated
    from rest_framework_simplejwt.authentication import JWTAuthentication
    from frago.views import ParallelChunkedUploadView

    class SecureUploadView(ParallelChunkedUploadView):
        authentication_classes = [JWTAuthentication]
        permission_classes = [IsAuthenticated]

🧠 Overriding the Identifier

    class MyView(ParallelChunkedUploadView):
        authentication_classes = [CustomAuthentication]
        
        identifier_field = 'device'

        def get_identifier(self, request):
            if self.IDENTIFIER_MODE == 'device':
                
                device_id = request.user

                if not device_id:
                    raise PermissionDenied("Device ID not found.")

                try:
                    device = Devices.objects.get(inventory_id=device_id)
                    print('device',device.id)
                    return device  # or another unique identifier
                except Devices.DoesNotExist:
                    raise PermissionDenied("Invalid Device ID.")

            # fallback to parent logic
            return super().get_identifier(request)
        

Update your urls.py:

    urlpatterns = [
        path('upload/',MyView.as_view()),
        path('upload/<uuid:pk>/', MyView.as_view()),
    ]

🔧 Custom Upload Model Example

    class Mychunkupload(AbstractChunkUpload):
        device = models.ForeignKey(Devices,on_delete=models.CASCADE)
        

        def assembled_path(self):
            try:
                device = Devices.objects.get(inventory_id=self.device_id)
                base_path = device.videos or settings.ASSEMBLED_PATH
            except Exception as e:
                base_path = settings.ASSEMBLED_PATH
            return os.path.join(base_path, f'{self.filename}')
        

Register it in settings:

    CHUNKED_UPLOADER_UPLOAD_MODEL = 'fragotest.Mychunkupload'

and specify the identifier field in the view

    identifier_field = 'device'

🔗 Uploader Client

Use the official uploader client:
👉 frago-client

📄 License

This project is licensed under the MIT License.

🤝 Contributing

Pull requests are welcome! 
Please open an issue first to discuss your idea.
Make sure to add tests for any new features or logic changes.

🙌 Author

Built with ❤️ to support scalable file upload workflows in Django.
Let me know if you want:
    Markdown preview badges
    GitHub Actions/test coverage
    Python client script (httpx/requests)

Metadata

Release files for frago 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for frago 0.1.4
File Size Uploaded
frago-0.1.4.tar.gz 14.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for frago 0.1.4
File Interpreter ABI Platform
frago-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 27.7 kB

Release files / frago-0.1.4.tar.gz

Download URL frago-0.1.4.tar.gz
Size 14.1 kB
Tags Source
SHA-256 checksum
How to use checksums
327faee6b65a94c96ca5ec79a6c71f382c088012c10624c9dead82eb0f789b2c
BLAKE2b-256 checksum
How to use checksums
83f03b4c58bb565b9b4323efe7629cbaf0d3322f4933a37f409df2fef9339a26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 11, 2025.

Transparency log

Release files / frago-0.1.4-py3-none-any.whl

Download URL frago-0.1.4-py3-none-any.whl
Size 13.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
967c9fca5686d057e4dd33501a15aa39bbe1a94a829786119275eb69cacadec1
BLAKE2b-256 checksum
How to use checksums
4de943600c801afee8c7682d33bfa0d96a478b84002d2d71fc593d0fe500fd24
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 11, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 release files

0.1.3

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page