11"""
22Stores daily data from ACRCloud's broadcast monitoring service in ownCloud.
33"""
4+ import json
5+ import os
46from datetime import datetime , timedelta
5- from logging import getLogger
67from functools import cache
7- import os
8- import json
8+ from io import BytesIO
9+ from logging import getLogger
910
11+ import urllib3
1012from acrclient import Client as ACRClient
1113from acrclient .models import GetBmCsProjectsResultsParams
1214from configargparse import ArgParser # type: ignore
15+ from minio import Minio # type: ignore
16+ from minio .error import S3Error # type: ignore
1317from owncloud import Client as OwnCloudClient # type: ignore
1418from owncloud .owncloud import HTTPResponseError as OCResponseError # type: ignore
1519from tqdm import tqdm
1620
17-
1821logger = getLogger (__name__ )
1922
2023
@@ -47,7 +50,7 @@ def oc_file_exists(oc: OwnCloudClient, path: str):
4750 return False
4851
4952
50- def check (oc : OwnCloudClient , oc_path : str ) -> list [datetime ]:
53+ def oc_check (oc : OwnCloudClient , oc_path : str ) -> list [datetime ]:
5154 """
5255 Checks ownCloud for missing files.
5356 """
@@ -71,34 +74,95 @@ def check(oc: OwnCloudClient, oc_path: str) -> list[datetime]:
7174 return missing
7275
7376
74- def fetch (
77+ def mc_check (mc : Minio , bucket : str ) -> list [datetime ]:
78+ """
79+ Checks MinIO for missing files.
80+ """
81+ missing = []
82+ start = datetime .now () - timedelta (7 )
83+ for requested in tqdm (daterange (start , datetime .now ()), desc = "Checking MinIO" ):
84+ try :
85+ mc .stat_object (bucket , requested .strftime ("%Y-%m-%d.json" ))
86+ except S3Error as ex :
87+ if ex .code == "NoSuchKey" :
88+ missing .append (requested )
89+ return missing
90+
91+
92+ @cache
93+ def fetch_one (
94+ acr : ACRClient ,
95+ acr_project_id : str ,
96+ acr_stream_id : str ,
97+ requested : str ,
98+ ):
99+ return acr .get_bm_cs_projects_results (
100+ project_id = int (acr_project_id ),
101+ stream_id = acr_stream_id ,
102+ params = GetBmCsProjectsResultsParams (
103+ type = "day" ,
104+ date = requested ,
105+ min_duration = 0 ,
106+ max_duration = 3600 ,
107+ isrc_country = "" ,
108+ ),
109+ )
110+
111+
112+ def oc_fetch (
75113 missing : list [datetime ],
76114 acr : ACRClient ,
77115 oc : OwnCloudClient ,
78116 acr_project_id : str ,
79117 acr_stream_id : str ,
80118 oc_path : str ,
81119):
82- """Fetches missing data from ACRCloud and stores it."""
83- for requested in tqdm (missing , desc = "Loading from ACRCloud" ):
120+ """Fetches missing data from ACRCloud and stores it in ownCloud ."""
121+ for requested in tqdm (missing , desc = "Loading into ownCloud from ACRCloud" ):
84122 target = os .path .join (
85123 oc_path ,
86124 str (requested .year ),
87125 str (requested .month ),
88126 requested .strftime ("%Y-%m-%d.json" ),
89127 )
90- data = acr . get_bm_cs_projects_results (
91- project_id = int ( acr_project_id ) ,
92- stream_id = acr_stream_id ,
93- params = GetBmCsProjectsResultsParams (
94- type = "day" ,
95- date = requested . strftime ( "%Y%m%d" ) ,
96- min_duration = 0 ,
97- max_duration = 3600 ,
98- isrc_country = "" ,
128+ oc . put_file_contents (
129+ target ,
130+ json . dumps (
131+ fetch_one (
132+ acr = acr ,
133+ acr_project_id = acr_project_id ,
134+ acr_stream_id = acr_stream_id ,
135+ requested = requested . strftime ( "%Y%m%d" ) ,
136+ )
99137 ),
100138 )
101- oc .put_file_contents (target , json .dumps (data ))
139+
140+
141+ def mc_fetch (
142+ missing : list [datetime ],
143+ acr : ACRClient ,
144+ mc : Minio ,
145+ acr_project_id : str ,
146+ acr_stream_id : str ,
147+ bucket : str ,
148+ ):
149+ """Fetches missing data from ACRCloud and stores it in MinIO."""
150+ for requested in tqdm (missing , desc = "Loading into MinIO from ACRCloud" ):
151+ _as_bytes = json .dumps (
152+ fetch_one (
153+ acr = acr ,
154+ acr_project_id = acr_project_id ,
155+ acr_stream_id = acr_stream_id ,
156+ requested = requested .strftime ("%Y%m%d" ),
157+ )
158+ ).encode ("utf-8" )
159+ mc .put_object (
160+ bucket ,
161+ requested .strftime ("%Y-%m-%d.json" ),
162+ BytesIO (_as_bytes ),
163+ length = len (_as_bytes ),
164+ content_type = "application/json" ,
165+ )
102166
103167
104168def main (): # pragma: no cover
@@ -134,6 +198,13 @@ def main(): # pragma: no cover
134198 env_var = "ACR_STREAM_ID" ,
135199 help = "ACRCloud stream id" ,
136200 )
201+ p .add (
202+ "--oc" ,
203+ default = False ,
204+ action = "store_true" ,
205+ env_var = "OC_ENABLE" ,
206+ help = "Enable ownCloud" ,
207+ )
137208 p .add (
138209 "--oc-url" ,
139210 default = "https://share.rabe.ch" ,
@@ -158,29 +229,106 @@ def main(): # pragma: no cover
158229 env_var = "OC_PATH" ,
159230 help = "ownCloud path" ,
160231 )
232+ p .add (
233+ "--minio" ,
234+ default = False ,
235+ action = "store_true" ,
236+ env_var = "MINIO_URL" ,
237+ help = "MinIO URL" ,
238+ )
239+ p .add (
240+ "--minio-url" ,
241+ default = "minio.service.int.rabe.ch:9000" ,
242+ env_var = "MINIO_HOST" ,
243+ help = "MinIO Hostname" ,
244+ )
245+ p .add (
246+ "--minio-secure" ,
247+ default = True ,
248+ env_var = "MINIO_SECURE" ,
249+ help = "MinIO Secure param" ,
250+ )
251+ p .add (
252+ "--minio-cert-reqs" ,
253+ default = "CERT_REQUIRED" ,
254+ env_var = "MINIO_CERT_REQS" ,
255+ help = "cert_reqs for urlib3.PoolManager used by MinIO" ,
256+ )
257+ p .add (
258+ "--minio-ca-certs" ,
259+ default = "/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt" ,
260+ env_var = "MINIO_CA_CERTS" ,
261+ help = "ca_certs for urlib3.PoolManager used by MinIO" ,
262+ )
263+ p .add (
264+ "--minio-bucket" ,
265+ default = "acrcloud.raw" ,
266+ env_var = "MINIO_BUCKET" ,
267+ help = "MinIO Bucket Name" ,
268+ )
269+ p .add (
270+ "--minio-access-key" ,
271+ default = None ,
272+ env_var = "MINIO_ACCESS_KEY" ,
273+ help = "MinIO Access Key" ,
274+ )
275+ p .add (
276+ "--minio-secret-key" ,
277+ default = None ,
278+ env_var = "MINIO_SECRET_KEY" ,
279+ help = "MinIO Secret Key" ,
280+ )
161281 options = p .parse_args ()
162-
163- # figure out what we are missing on ownCloud
164- oc = OwnCloudClient (options .oc_url )
165- oc .login (options .oc_user , options .oc_pass )
166- missing = check (oc = oc , oc_path = options .oc_path )
167-
168- # return early if no files are missing
169- if not missing :
170- return
171-
172- # fetch and store missing data
173282 acr_client = ACRClient (
174283 bearer_token = options .acr_bearer_token ,
175284 )
176- fetch (
177- missing ,
178- acr = acr_client ,
179- oc = oc ,
180- acr_project_id = options .acr_project_id ,
181- acr_stream_id = options .acr_stream_id ,
182- oc_path = options .oc_path ,
183- )
285+
286+ if options .oc :
287+ # figure out what we are missing on ownCloud
288+ oc = OwnCloudClient (options .oc_url )
289+ oc .login (options .oc_user , options .oc_pass )
290+ missing = oc_check (oc = oc , oc_path = options .oc_path )
291+
292+ # return early if no files are missing
293+ if not missing :
294+ return
295+
296+ # fetch and store missing data
297+ oc_fetch (
298+ missing ,
299+ acr = acr_client ,
300+ oc = oc ,
301+ acr_project_id = options .acr_project_id ,
302+ acr_stream_id = options .acr_stream_id ,
303+ oc_path = options .oc_path ,
304+ )
305+ if options .minio :
306+ mc = Minio (
307+ options .minio_url ,
308+ options .minio_access_key ,
309+ options .minio_secret_key ,
310+ secure = options .minio_secure ,
311+ http_client = urllib3 .PoolManager (
312+ cert_reqs = options .minio_cert_reqs , ca_certs = options .minio_ca_certs
313+ ),
314+ )
315+ if not mc .bucket_exists (options .minio_bucket ):
316+ mc .make_bucket (options .minio_bucket )
317+ missing = mc_check (mc = mc , bucket = options .minio_bucket )
318+
319+ # return early if no files are missing
320+ if not missing :
321+ return
322+
323+ # fetch and store missing data
324+ mc_fetch (
325+ missing ,
326+ acr = acr_client ,
327+ mc = mc ,
328+ acr_project_id = options .acr_project_id ,
329+ acr_stream_id = options .acr_stream_id ,
330+ bucket = options .bucket ,
331+ )
184332
185333
186334if __name__ == "__main__" : # pragma: no cover
0 commit comments