Add Atlos storage

pull/137/head
R. Miles McCain 2024-03-31 14:27:55 +02:00
rodzic 10455edcf7
commit 2750b44277
8 zmienionych plików z 66 dodań i 15 usunięć

Wyświetl plik

@ -25,10 +25,11 @@ class Media:
_mimetype: str = None # eg: image/jpeg
_stored: bool = field(default=False, repr=False, metadata=config(exclude=lambda _: True)) # always exclude
def store(self: Media, override_storages: List = None, url: str = "url-not-available"):
# stores the media into the provided/available storages [Storage]
# repeats the process for its properties, in case they have inner media themselves
# for now it only goes down 1 level but it's easy to make it recursive if needed
def store(self: Media, override_storages: List = None, url: str = "url-not-available", metadata: Any = None):
# 'Any' typing for metadata to avoid circular imports. Stores the media
# into the provided/available storages [Storage] repeats the process for
# its properties, in case they have inner media themselves for now it
# only goes down 1 level but it's easy to make it recursive if needed.
storages = override_storages or ArchivingContext.get("storages")
if not len(storages):
logger.warning(f"No storages found in local context or provided directly for {self.filename}.")
@ -36,7 +37,7 @@ class Media:
for s in storages:
for any_media in self.all_inner_media(include_self=True):
s.store(any_media, url)
s.store(any_media, url, metadata=metadata)
def all_inner_media(self, include_self=False):
""" Media can be inside media properties, examples include transformations on original media.

Wyświetl plik

@ -48,7 +48,7 @@ class Metadata:
self.remove_duplicate_media_by_hash()
storages = override_storages or ArchivingContext.get("storages")
for media in self.media:
media.store(override_storages=storages, url=self.get_url())
media.store(override_storages=storages, url=self.get_url(), metadata=self)
def set(self, key: str, val: Any) -> Metadata:
self.metadata[key] = val

Wyświetl plik

@ -120,7 +120,7 @@ class ArchivingOrchestrator:
# 6 - format and store formatted if needed
if (final_media := self.formatter.format(result)):
final_media.store(url=url)
final_media.store(url=url, metadata=result)
result.set_final_media(final_media)
if result.is_empty():

Wyświetl plik

@ -44,7 +44,7 @@ class WhisperEnricher(Enricher):
job_results = {}
for i, m in enumerate(to_enrich.media):
if m.is_video() or m.is_audio():
m.store(url=url)
m.store(url=url, metadata=to_enrich)
try:
job_id = self.submit_job(m)
job_results[job_id] = False

Wyświetl plik

@ -36,9 +36,10 @@ class AtlosFeeder(Feeder):
for item in data["results"]:
if (
item["source_url"] not in [None, ""]
and item["metadata"]
and (item["metadata"]
.get("auto_archiver", {})
.get("processed", False) != True
.get("processed", False) != True or True)
and item["visibility"] == "visible"
):
yield Metadata().set_url(item["source_url"]).set(
"atlos_id", item["id"]

Wyświetl plik

@ -1,4 +1,5 @@
from .storage import Storage
from .s3 import S3Storage
from .local import LocalStorage
from .gd import GDriveStorage
from .gd import GDriveStorage
from .atlos import AtlosStorage

Wyświetl plik

@ -0,0 +1,48 @@
import os
from typing import IO, List, Optional
from loguru import logger
import requests
from ..core import Media, Metadata
from ..storages import Storage
from ..utils import get_atlos_config_options
class AtlosStorage(Storage):
name = "atlos_storage"
def __init__(self, config: dict) -> None:
super().__init__(config)
@staticmethod
def configs() -> dict:
return dict(Storage.configs(), **get_atlos_config_options())
def get_cdn_url(self, _media: Media) -> str:
# It's not always possible to provide an exact URL, because it's
# possible that the media once uploaded could have been copied to
# another project.
return self.atlos_url
def upload(self, media: Media, metadata: Optional[Metadata]=None, **_kwargs) -> bool:
atlos_id = metadata.get("atlos_id")
if atlos_id is None:
logger.error(f"No Atlos ID found in metadata; can't store {media.filename} on Atlos")
return False
# Upload the media to the Atlos API
requests.post(
f"{self.atlos_url}/api/v2/source_material/upload/{atlos_id}",
headers={"Authorization": f"Bearer {self.api_token}"},
json={
"title": media.key
},
files={"file": (os.path.basename(media.filename), open(media.filename, "rb"))},
).raise_for_status()
logger.info(f"Uploaded {media.filename} to Atlos with ID {atlos_id} and title {media.key}")
return True
# must be implemented even if unused
def uploadf(self, file: IO[bytes], key: str, **kwargs: dict) -> bool: pass

Wyświetl plik

@ -1,12 +1,12 @@
from __future__ import annotations
from abc import abstractmethod
from dataclasses import dataclass
from typing import IO
from typing import IO, Optional
import os
from ..utils.misc import random_str
from ..core import Media, Step, ArchivingContext
from ..core import Media, Step, ArchivingContext, Metadata
from ..enrichers import HashEnricher
from loguru import logger
from slugify import slugify
@ -43,12 +43,12 @@ class Storage(Step):
# only for typing...
return Step.init(name, config, Storage)
def store(self, media: Media, url: str) -> None:
def store(self, media: Media, url: str, metadata: Optional[Metadata]=None) -> None:
if media.is_stored():
logger.debug(f"{media.key} already stored, skipping")
return
self.set_key(media, url)
self.upload(media)
self.upload(media, metadata=metadata)
media.add_url(self.get_cdn_url(media))
@abstractmethod