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)

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

frago-0.1.4.tar.gz (14.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

frago-0.1.4-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

Details for the file frago-0.1.4.tar.gz.

File metadata

  • Download URL: frago-0.1.4.tar.gz
  • Upload date:
  • Size: 14.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for frago-0.1.4.tar.gz
Algorithm Hash digest
SHA256 327faee6b65a94c96ca5ec79a6c71f382c088012c10624c9dead82eb0f789b2c
MD5 d9f0ab956d16af7a9703c6805bb7ee01
BLAKE2b-256 83f03b4c58bb565b9b4323efe7629cbaf0d3322f4933a37f409df2fef9339a26

See more details on using hashes here.

Provenance

The following attestation bundles were made for frago-0.1.4.tar.gz:

Publisher: publish.yml on Albinm123/frago

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file frago-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: frago-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 13.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for frago-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 967c9fca5686d057e4dd33501a15aa39bbe1a94a829786119275eb69cacadec1
MD5 f82559c895ace92f151c477bc140f3e8
BLAKE2b-256 4de943600c801afee8c7682d33bfa0d96a478b84002d2d71fc593d0fe500fd24

See more details on using hashes here.

Provenance

The following attestation bundles were made for frago-0.1.4-py3-none-any.whl:

Publisher: publish.yml on Albinm123/frago

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 files

0.1.3

2 files

0.1.2

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page