# Path: rep0st/db/post.py
# class Post(Base):
#   from rep0st.db.feature import Feature
#   from rep0st.db.tag import Tag
# 
#   __tablename__ = 'post'
#   # Post id.
#   id = Column(Integer, primary_key=True, index=True, autoincrement=False)
#   # Timestamp this post was created.
#   created = Column(DateTime(), nullable=False)
#   # Path of the image on pr0gramm servers.
#   image = Column(String(256), nullable=False, index=True)
#   # Path of the thumbnail on pr0gramm servers.
#   thumb = Column(String(256), nullable=False)
#   # Path of the fullsize image on pr0gramm servers. Optional.
#   fullsize = Column(String(256))
#   # Width of the media in pixels.
#   width = Column(Integer(), nullable=False)
#   # Height of the media in pixels.
#   height = Column(Integer(), nullable=False)
#   # True if the media has audio.
#   audio = Column(Boolean(), nullable=False)
#   # URL of the source of the image. Optional.
#   source = Column(String(512))
#   # Flags for the post encoded as a bitset. If the bit is set,
#   # the post is marked with the given tag.
#   # Bit 0: SFW
#   # Bit 1: NSFW
#   # Bit 2: NSFL
#   # Bit 3: NSFP
#   flags = Column(Integer(), nullable=False)
#   # Name of the user that uploaded the post.
#   user = Column(String(32), nullable=False)
#   # Type of the media in the post.
#   # - IMAGE: Static images. (jpg, png)
#   # - ANIMATED: Animated images. (gif)
#   # - VIDEO: Videos. (mp4, webm)
#   type = Column(Enum(Type), nullable=False, index=True)
#   # Status of the post.
#   status = Column(
#       Enum(Status), nullable=False, index=True, default=Status.NO_MEDIA)
#   # True if the post is deleted on pr0gramm.
#   deleted = Column(Boolean(), nullable=False, default=False)
#   # List of features associated with this post.
#   features = relationship(Feature)
#   # List of tags associated with this post.
#   tags = relationship(Tag)
# 
#   def __json__(self):
#     return {
#         'id': self.id,
#         'user': self.user,
#         'created': self.created.isoformat(),
#         'is_sfw': self.is_sfw(),
#         'is_nsfw': self.is_nsfw(),
#         'is_nsfl': self.is_nsfl(),
#         'image': self.image,
#         'thumb': self.thumb,
#         'fullsize': self.fullsize,
#     }
# 
#   def as_indexed_doc(self):
# 
#     def feauture_key_func(feature: Feature):
#       return feature.id
# 
#     return {
#         'meta': {
#             'id': self.id
#         },
#         'created':
#             int(self.created.timestamp() * 1000),
#         'flags': [flag.value for flag in self.get_flags()],
#         'type':
#             self.type.value,
#         'tags': [tag.tag for tag in self.tags],
#         'frames': [{
#             'id': key,
#             'features': {v.type: v.data for v in valuesiter}
#         } for key, valuesiter in groupby(
#             sorted(self.features, key=feauture_key_func), key=feauture_key_func)
#                   ],
#     }
# 
#   def is_sfw(self):
#     return self.flags & 1 != 0
# 
#   def is_nsfw(self):
#     return self.flags & 2 != 0
# 
#   def is_nsfl(self):
#     return self.flags & 4 != 0
# 
#   def is_nsfp(self):
#     return self.flags & 8 != 0
# 
#   def get_flags(self) -> List[Flag]:
#     flags = []
#     if self.is_sfw():
#       flags.append(Flag.SFW)
#     if self.is_nsfw():
#       flags.append(Flag.NSFW)
#     if self.is_nsfl():
#       flags.append(Flag.NSFL)
#     if self.is_nsfp():
#       flags.append(Flag.NSFP)
#     return flags
# 
#   def get_flag_by_importance(self) -> Flag:
#     if self.is_nsfl():
#       return Flag.NSFL
#     if self.is_nsfw():
#       return Flag.NSFW
#     if self.is_nsfp():
#       return Flag.NSFP
#     return Flag.SFW
# 
#   def __str__(self):
#     return "Post(id=" + str(self.id) + ")"
# 
#   def __repr__(self):
#     return "Post(id=" + str(self.id) + ")"
# 
# class Type(enum.Enum):
#   IMAGE = 'IMAGE'
#   ANIMATED = 'ANIMATED'
#   VIDEO = 'VIDEO'
#   UNKNOWN = 'UNKNOWN'


import logging
import numpy
import ffmpeg
import subprocess
from pathlib import Path
from typing import BinaryIO, Callable, Dict, Iterable, NewType, Union, IO
from absl import flags
from cv2.cv2 import IMREAD_COLOR, imdecode, cvtColor, COLOR_RGB2BGR
from injector import Binder, Module, inject, singleton
from rep0st.db.post import Post, Type


class ReadMediaServiceModule(Module):

  def configure(self, binder: Binder):
    binder.install(DecodeMediaServiceModule)
    binder.install(_MediaFlagModule)
    binder.bind(ReadMediaService)


class NoMediaFoundException(Exception):
  pass


class ImageDecodeException(Exception):
  pass


@singleton
class ReadMediaService:
  media_dir: Path
  decode_media_service: DecodeMediaService
  decoders: Dict[Type, Callable[[Iterable[numpy.ndarray]], BinaryIO]]

  @inject
  def __init__(self, media_dir: _MediaDirectory,
               decode_media_service: DecodeMediaService):
    self.media_dir = media_dir
    self.decode_media_service = decode_media_service
    self.decoders = {

# Path: rep0st/db/post.py
# class Post(Base):
#   from rep0st.db.feature import Feature
#   from rep0st.db.tag import Tag
# 
#   __tablename__ = 'post'
#   # Post id.
#   id = Column(Integer, primary_key=True, index=True, autoincrement=False)
#   # Timestamp this post was created.
#   created = Column(DateTime(), nullable=False)
#   # Path of the image on pr0gramm servers.
#   image = Column(String(256), nullable=False, index=True)
#   # Path of the thumbnail on pr0gramm servers.
#   thumb = Column(String(256), nullable=False)
#   # Path of the fullsize image on pr0gramm servers. Optional.
#   fullsize = Column(String(256))
#   # Width of the media in pixels.
#   width = Column(Integer(), nullable=False)
#   # Height of the media in pixels.
#   height = Column(Integer(), nullable=False)
#   # True if the media has audio.
#   audio = Column(Boolean(), nullable=False)
#   # URL of the source of the image. Optional.
#   source = Column(String(512))
#   # Flags for the post encoded as a bitset. If the bit is set,
#   # the post is marked with the given tag.
#   # Bit 0: SFW
#   # Bit 1: NSFW
#   # Bit 2: NSFL
#   # Bit 3: NSFP
#   flags = Column(Integer(), nullable=False)
#   # Name of the user that uploaded the post.
#   user = Column(String(32), nullable=False)
#   # Type of the media in the post.
#   # - IMAGE: Static images. (jpg, png)
#   # - ANIMATED: Animated images. (gif)
#   # - VIDEO: Videos. (mp4, webm)
#   type = Column(Enum(Type), nullable=False, index=True)
#   # Status of the post.
#   status = Column(
#       Enum(Status), nullable=False, index=True, default=Status.NO_MEDIA)
#   # True if the post is deleted on pr0gramm.
#   deleted = Column(Boolean(), nullable=False, default=False)
#   # List of features associated with this post.
#   features = relationship(Feature)
#   # List of tags associated with this post.
#   tags = relationship(Tag)
# 
#   def __json__(self):
#     return {
#         'id': self.id,
#         'user': self.user,
#         'created': self.created.isoformat(),
#         'is_sfw': self.is_sfw(),
#         'is_nsfw': self.is_nsfw(),
#         'is_nsfl': self.is_nsfl(),
#         'image': self.image,
#         'thumb': self.thumb,
#         'fullsize': self.fullsize,
#     }
# 
#   def as_indexed_doc(self):
# 
#     def feauture_key_func(feature: Feature):
#       return feature.id
# 
#     return {
#         'meta': {
#             'id': self.id
#         },
#         'created':
#             int(self.created.timestamp() * 1000),
#         'flags': [flag.value for flag in self.get_flags()],
#         'type':
#             self.type.value,
#         'tags': [tag.tag for tag in self.tags],
#         'frames': [{
#             'id': key,
#             'features': {v.type: v.data for v in valuesiter}
#         } for key, valuesiter in groupby(
#             sorted(self.features, key=feauture_key_func), key=feauture_key_func)
#                   ],
#     }
# 
#   def is_sfw(self):
#     return self.flags & 1 != 0
# 
#   def is_nsfw(self):
#     return self.flags & 2 != 0
# 
#   def is_nsfl(self):
#     return self.flags & 4 != 0
# 
#   def is_nsfp(self):
#     return self.flags & 8 != 0
# 
#   def get_flags(self) -> List[Flag]:
#     flags = []
#     if self.is_sfw():
#       flags.append(Flag.SFW)
#     if self.is_nsfw():
#       flags.append(Flag.NSFW)
#     if self.is_nsfl():
#       flags.append(Flag.NSFL)
#     if self.is_nsfp():
#       flags.append(Flag.NSFP)
#     return flags
# 
#   def get_flag_by_importance(self) -> Flag:
#     if self.is_nsfl():
#       return Flag.NSFL
#     if self.is_nsfw():
#       return Flag.NSFW
#     if self.is_nsfp():
#       return Flag.NSFP
#     return Flag.SFW
# 
#   def __str__(self):
#     return "Post(id=" + str(self.id) + ")"
# 
#   def __repr__(self):
#     return "Post(id=" + str(self.id) + ")"
# 
# class Type(enum.Enum):
#   IMAGE = 'IMAGE'
#   ANIMATED = 'ANIMATED'
#   VIDEO = 'VIDEO'
#   UNKNOWN = 'UNKNOWN'


# Path: rep0st/service/media_service.py
import logging
import numpy
import ffmpeg
import subprocess
from pathlib import Path
from typing import BinaryIO, Callable, Dict, Iterable, NewType, Union, IO
from absl import flags
from cv2.cv2 import IMREAD_COLOR, imdecode, cvtColor, COLOR_RGB2BGR
from injector import Binder, Module, inject, singleton
from rep0st.db.post import Post, Type



class ReadMediaServiceModule(Module):

  def configure(self, binder: Binder):
    binder.install(DecodeMediaServiceModule)
    binder.install(_MediaFlagModule)
    binder.bind(ReadMediaService)


class NoMediaFoundException(Exception):
  pass


class ImageDecodeException(Exception):
  pass


@singleton
class ReadMediaService:
  media_dir: Path
  decode_media_service: DecodeMediaService
  decoders: Dict[Type, Callable[[Iterable[numpy.ndarray]], BinaryIO]]

  @inject
  def __init__(self, media_dir: _MediaDirectory,
               decode_media_service: DecodeMediaService):
    self.media_dir = media_dir
    self.decode_media_service = decode_media_service
    self.decoders = {

        Type.IMAGE: self.decode_media_service.decode_image_from_file,