Coverage for seedboxsync/core/database/models/download.py: 69%
36 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
1#
2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr>
3#
4# For the full copyright and license information, please view the LICENSE
5# file that was distributed with this source code.
6#
7"""Peewee DAO model for Download."""
9from collections.abc import Iterable
10import datetime
11from typing import Any, cast
12from peewee import AutoField, CharField, DateTimeField, IntegerField, TextField, fn
13from seedboxsync.core import utils
14from seedboxsync.core.database.models import SeedboxSyncModel
17class Download(SeedboxSyncModel):
18 """
19 Data Access Object (DAO) representing a file download.
21 This model stores information about a downloaded file, including its path,
22 size on the seedbox and locally, as well as timestamps indicating when
23 the download started and finished.
24 """
26 id = AutoField(help_text="Unique identifier of the download")
27 path = TextField(help_text="Path of the downloaded file")
28 seedbox_size = IntegerField(help_text="Size of the file on the seedbox in bytes")
29 local_size = IntegerField(default=0, help_text="Size of the downloaded file stored locally in bytes")
30 mime_extension = CharField(max_length=255, default="", help_text="File extension detected during MIME analysis")
31 mime_type = CharField(max_length=100, default="", help_text="MIME type detected for the file (e.g. video/mp4)")
32 mime_confidence = CharField(max_length=65, default="", help_text="Confidence level or method used for MIME detection (e.g. extension, magic)")
33 started = DateTimeField(default=datetime.datetime.now, help_text="Timestamp when the download started")
34 finished = DateTimeField(default=0, help_text="Timestamp when the download finished")
36 def set_mime(self, save: bool = False) -> None:
37 """
38 Detect and update the MIME attributes of the downloaded file.
40 Calls ``utils.get_mime_type_from_file`` to inspect the file path and header bytes,
41 updating the ``mime_extension``, ``mime_type``, and ``mime_confidence`` attributes.
43 Args:
44 save (bool, optional): If True, saves the model instance to the database
45 immediately after updating attributes. Defaults to False.
46 """
47 self.mime_type, self.mime_extension, self.mime_confidence = utils.get_mime_type_from_file(self.path)
49 if save:
50 self.save()
52 @classmethod
53 def is_already_download(cls, filepath: str) -> bool:
54 """
55 Check if a file has already been downloaded.
57 Args:
58 filepath (str): Absolute or relative path to the file.
60 Returns:
61 bool: True if the file was already downloaded (i.e. has a nonzero
62 ``finished`` timestamp), otherwise False.
63 """
64 count = cls.select().where(cls.path == filepath, cls.finished > 0).count()
65 return count != 0
67 @classmethod
68 def get_stats_by_mime_type(cls, start_date: datetime.date | None = None, end_date: datetime.date | None = None) -> list[dict[str, Any]]:
69 """
70 Get the total count and total size of downloaded files grouped by MIME type.
72 Args:
73 start_date (datetime.date | None): Optional start date filter.
74 end_date (datetime.date | None): Optional end date filter.
76 Returns:
77 list[dict[str, Any]]: A list of dicts containing mime_type, total count, and total_size.
78 """
79 # Build "where" expression
80 conditions = []
81 if start_date:
82 conditions.append(cls.finished >= start_date)
83 if end_date:
84 conditions.append(cls.finished <= end_date)
86 query = (
87 cls.select(
88 cls.mime_type,
89 fn.COUNT(cls.id).alias("total"),
90 fn.SUM(cls.local_size).alias("total_size"),
91 fn.humanize(fn.SUM(cls.local_size)).alias("human_total_size"),
92 )
93 .group_by(cls.mime_type)
94 .order_by(fn.SUM(cls.local_size).desc())
95 )
97 # if "where" expression
98 if conditions:
99 query = query.where(*conditions)
100 data = query.dicts()
102 return list(cast(Iterable[dict[str, Any]], data))