|
65 | 65 | logger = logging.getLogger(__name__)
|
66 | 66 |
|
67 | 67 |
|
68 |
| -UPDATE_RECENTLY_ACCESSED_TS = 60 * 1000 |
| 68 | +# How often to run the background job to update the "recently accessed" |
| 69 | +# attribute of local and remote media. |
| 70 | +UPDATE_RECENTLY_ACCESSED_TS = 60 * 1000 # 1 minute |
| 71 | +# How often to run the background job to check for local and remote media |
| 72 | +# that should be purged according to the configured media retention settings. |
| 73 | +MEDIA_RETENTION_CHECK_PERIOD_MS = 60 * 60 * 1000 # 1 hour |
69 | 74 |
|
70 | 75 |
|
71 | 76 | class MediaRepository:
|
@@ -122,11 +127,36 @@ def __init__(self, hs: "HomeServer"):
|
122 | 127 | self._start_update_recently_accessed, UPDATE_RECENTLY_ACCESSED_TS
|
123 | 128 | )
|
124 | 129 |
|
| 130 | + # Media retention configuration options |
| 131 | + self._media_retention_local_media_lifetime_ms = ( |
| 132 | + hs.config.media.media_retention_local_media_lifetime_ms |
| 133 | + ) |
| 134 | + self._media_retention_remote_media_lifetime_ms = ( |
| 135 | + hs.config.media.media_retention_remote_media_lifetime_ms |
| 136 | + ) |
| 137 | + |
| 138 | + # Check whether local or remote media retention is configured |
| 139 | + if ( |
| 140 | + hs.config.media.media_retention_local_media_lifetime_ms is not None |
| 141 | + or hs.config.media.media_retention_remote_media_lifetime_ms is not None |
| 142 | + ): |
| 143 | + # Run the background job to apply media retention rules routinely, |
| 144 | + # with the duration between runs dictated by the homeserver config. |
| 145 | + self.clock.looping_call( |
| 146 | + self._start_apply_media_retention_rules, |
| 147 | + MEDIA_RETENTION_CHECK_PERIOD_MS, |
| 148 | + ) |
| 149 | + |
125 | 150 | def _start_update_recently_accessed(self) -> Deferred:
|
126 | 151 | return run_as_background_process(
|
127 | 152 | "update_recently_accessed_media", self._update_recently_accessed
|
128 | 153 | )
|
129 | 154 |
|
| 155 | + def _start_apply_media_retention_rules(self) -> Deferred: |
| 156 | + return run_as_background_process( |
| 157 | + "apply_media_retention_rules", self._apply_media_retention_rules |
| 158 | + ) |
| 159 | + |
130 | 160 | async def _update_recently_accessed(self) -> None:
|
131 | 161 | remote_media = self.recently_accessed_remotes
|
132 | 162 | self.recently_accessed_remotes = set()
|
@@ -835,6 +865,45 @@ async def _generate_thumbnails(
|
835 | 865 |
|
836 | 866 | return {"width": m_width, "height": m_height}
|
837 | 867 |
|
| 868 | + async def _apply_media_retention_rules(self) -> None: |
| 869 | + """ |
| 870 | + Purge old local and remote media according to the media retention rules |
| 871 | + defined in the homeserver config. |
| 872 | + """ |
| 873 | + # Purge remote media |
| 874 | + if self._media_retention_remote_media_lifetime_ms is not None: |
| 875 | + # Calculate a threshold timestamp derived from the configured lifetime. Any |
| 876 | + # media that has not been accessed since this timestamp will be removed. |
| 877 | + remote_media_threshold_timestamp_ms = ( |
| 878 | + self.clock.time_msec() - self._media_retention_remote_media_lifetime_ms |
| 879 | + ) |
| 880 | + |
| 881 | + logger.info( |
| 882 | + "Purging remote media last accessed before" |
| 883 | + f" {remote_media_threshold_timestamp_ms}" |
| 884 | + ) |
| 885 | + |
| 886 | + await self.delete_old_remote_media( |
| 887 | + before_ts=remote_media_threshold_timestamp_ms |
| 888 | + ) |
| 889 | + |
| 890 | + # And now do the same for local media |
| 891 | + if self._media_retention_local_media_lifetime_ms is not None: |
| 892 | + # This works the same as the remote media threshold |
| 893 | + local_media_threshold_timestamp_ms = ( |
| 894 | + self.clock.time_msec() - self._media_retention_local_media_lifetime_ms |
| 895 | + ) |
| 896 | + |
| 897 | + logger.info( |
| 898 | + "Purging local media last accessed before" |
| 899 | + f" {local_media_threshold_timestamp_ms}" |
| 900 | + ) |
| 901 | + |
| 902 | + await self.delete_old_local_media( |
| 903 | + before_ts=local_media_threshold_timestamp_ms, |
| 904 | + keep_profiles=True, |
| 905 | + ) |
| 906 | + |
838 | 907 | async def delete_old_remote_media(self, before_ts: int) -> Dict[str, int]:
|
839 | 908 | old_media = await self.store.get_remote_media_before(before_ts)
|
840 | 909 |
|
|
0 commit comments