#
#            PySceneDetect: Python-Based Video Scene Detector
#   -------------------------------------------------------------------
#     [  Site:    https://scenedetect.com                           ]
#     [  Docs:    https://scenedetect.com/docs/                     ]
#     [  Github:  https://github.com/Breakthrough/PySceneDetect/    ]
#
# Copyright (C) 2022 Brandon Castellano <http://www.bcastell.com>.
# PySceneDetect is licensed under the BSD 3-Clause License; see the
# included LICENSE file, or visit one of the above pages for details.
#
"""``scenedetect.backends`` Module

This module contains :class:`VideoStream <scenedetect.video_stream.VideoStream>` implementations
backed by various Python multimedia libraries. In addition to creating backend objects directly,
:func:`scenedetect.open_video` can be used to open a video with a specified backend, falling
back to OpenCV if not available.

All backends available on the current system can be found via :data:`AVAILABLE_BACKENDS`.

If you already have a `cv2.VideoCapture` object you want to use for scene detection, you can
use a :class:`VideoCaptureAdapter <scenedetect.backends.opencv.VideoCaptureAdapter>` instead
of a backend. This is useful when working with devices or streams, for example.

===============================================================
Video Files
===============================================================

Assuming we have a file `video.mp4` in our working directory, we can load it and perform scene
detection on it using :func:`open_video`:

.. code:: python

    from scenedetect import open_video
    video = open_video("video.mp4")

An optional backend from :data:`AVAILABLE_BACKENDS` can be passed to :func:`open_video`
(e.g. `backend="opencv"`). Additional keyword arguments passed to :func:`open_video`
will be forwarded to the backend constructor. If the specified backend is unavailable, or
loading the video fails, ``opencv`` will be tried as a fallback.

Lastly, to use a specific backend directly:

.. code:: python

    # Manually importing and constructing a backend:
    from scenedetect.backends.opencv import VideoStreamCv2
    video = VideoStreamCv2("video.mp4")

In both examples above, the resulting ``video`` can be used with
:meth:`SceneManager.detect_scenes() <scenedetect.scene_manager.SceneManager.detect_scenes>`.

Multiple videos can be opened as one continuous stream by passing a list of paths to
:func:`open_video`, which returns a
:class:`VideoStreamConcat <scenedetect.backends.concat.VideoStreamConcat>`:

.. code:: python

    video = open_video(["part1.mp4", "part2.mp4"])

===============================================================
Devices / Cameras / Pipes
===============================================================

You can use an existing `cv2.VideoCapture` object with the PySceneDetect API using a
:class:`VideoCaptureAdapter <scenedetect.backends.opencv.VideoCaptureAdapter>`. For example,
to use a :class:`SceneManager <scenedetect.scene_manager.SceneManager>` with a webcam device:

.. code:: python

    from scenedetect import SceneManager, ContentDetector
    from scenedetect.backends import VideoCaptureAdapter
    # Open device ID 2.
    cap = cv2.VideoCapture(2)
    video = VideoCaptureAdapter(cap)
    total_frames = 1000
    scene_manager = SceneManager()
    scene_manager.add_detector(ContentDetector())
    scene_manager.detect_scenes(video=video, duration=total_frames)

When working with live inputs, note that you can pass a callback to
:meth:`detect_scenes() <scenedetect.scene_manager.SceneManager.detect_scenes>` to be
called on every scene detection event. See the :mod:`SceneManager <scenedetect.scene_manager>`
examples for details.
"""

# TODO(v1.0): Consider removing and making this a namespace package so that additional backends can
# be dynamically added. The preferred approach for this should probably be:
# https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/#using-namespace-packages

# TODO: Future VideoStream implementations under consideration:
#  - Nvidia VPF: https://developer.nvidia.com/blog/vpf-hardware-accelerated-video-processing-framework-in-python/

# OpenCV must be available at minimum.
from scenedetect.backends.concat import SourceSpan as SourceSpan
from scenedetect.backends.concat import VideoStreamConcat as VideoStreamConcat
from scenedetect.backends.opencv import VideoCaptureAdapter as VideoCaptureAdapter
from scenedetect.backends.opencv import VideoStreamCv2 as VideoStreamCv2

try:
    from scenedetect.backends.pyav import VideoStreamAv as VideoStreamAv
except ImportError:
    VideoStreamAv = None

try:
    from scenedetect.backends.moviepy import VideoStreamMoviePy as VideoStreamMoviePy
except ImportError:
    VideoStreamMoviePy = None

# TODO: Lazy-loading backends would improve startup performance. However, this requires removing
# some of the re-exported types above from the public API.
AVAILABLE_BACKENDS: dict[str, type] = {
    backend.BACKEND_NAME: backend
    for backend in filter(
        None,
        [
            VideoStreamCv2,
            VideoStreamAv,
            VideoStreamMoviePy,
        ],
    )
}
"""All available backends that :func:`scenedetect.open_video` can consider for the `backend`
parameter. These backends must support construction with the following signature:

    BackendType(path: str, frame_rate: ty.Optional[float | Fraction])
"""
