diff --git a/README.md b/README.md index 3ab0e8f..8a87603 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,8 @@ Python module to, stream videos via authenticated sessions using FastAPI python -m pip install stream-localhost ``` -## Usage +## Sample Usage ```python -import asyncio import os import pystream @@ -29,7 +28,7 @@ if __name__ == '__main__': ) # Add the following to host on local IP address, skip for localhost (127.0.0.1) # kwargs["video_host"] = pystream.utils.get_local_ip() - asyncio.run(pystream.start(**kwargs)) + pystream.start(**kwargs) ``` ### Env Variables @@ -49,10 +48,6 @@ if __name__ == '__main__': - **WORKERS**: Number of workers to spin up the `uvicorn` server. Defaults to `1` - **WEBSITE**: Website to add to CORS configuration. _Required only if tunneled via CDN_ - **AUTO_THUMBNAIL**: Boolean flag to auto generate thumbnail images for preview. Defaults to `True` -- **SCAN_INTERVAL**: Interval in seconds to scan `VIDEO_SOURCE` in the background. Defaults traditional scan at run time. -> :bulb:   When `SCAN_INTERVAL` is set to `None`, `VIDEO_SOURCE` will be scanned in real time whenever the user -> loads the landing page. Setting a `SCAN_INTERVAL` will scan the `VIDEO_SOURCE` in background and reduce runtime -> delays in case of complex directory structure. ## Coding Standards Docstring format: [`Google`][google-docs]
diff --git a/doc_gen/index.rst b/doc_gen/index.rst index 154e6a4..9e405b6 100644 --- a/doc_gen/index.rst +++ b/doc_gen/index.rst @@ -42,13 +42,6 @@ Images :members: :undoc-members: -Scheduler -========= - -.. automodule:: pystream.models.scheduler - :members: - :undoc-members: - Squire ====== diff --git a/docs/README.html b/docs/README.html index c7a1d57..dfe99cc 100644 --- a/docs/README.html +++ b/docs/README.html @@ -57,10 +57,9 @@

Install -

Usage

-
import asyncio
-import os
+
+

Sample Usage

+
@@ -97,13 +96,7 @@

Env Variablesuvicorn server. Defaults to 1

  • WEBSITE: Website to add to CORS configuration. Required only if tunneled via CDN

  • AUTO_THUMBNAIL: Boolean flag to auto generate thumbnail images for preview. Defaults to True

  • -
  • SCAN_INTERVAL: Interval in seconds to scan VIDEO_SOURCE in the background. Defaults traditional scan at run time.

  • -
    -

    :bulb:   When SCAN_INTERVAL is set to None, VIDEO_SOURCE will be scanned in real time whenever the user -loads the landing page. Setting a SCAN_INTERVAL will scan the VIDEO_SOURCE in background and reduce runtime -delays in case of complex directory structure.

    -

    @@ -164,7 +157,7 @@

    Table of Contents

    • Video Streaming
      • Install
      • -
      • Usage
          +
        • Sample Usage
        • diff --git a/docs/README.md b/docs/README.md index 3ab0e8f..8a87603 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,9 +15,8 @@ Python module to, stream videos via authenticated sessions using FastAPI python -m pip install stream-localhost ``` -## Usage +## Sample Usage ```python -import asyncio import os import pystream @@ -29,7 +28,7 @@ if __name__ == '__main__': ) # Add the following to host on local IP address, skip for localhost (127.0.0.1) # kwargs["video_host"] = pystream.utils.get_local_ip() - asyncio.run(pystream.start(**kwargs)) + pystream.start(**kwargs) ``` ### Env Variables @@ -49,10 +48,6 @@ if __name__ == '__main__': - **WORKERS**: Number of workers to spin up the `uvicorn` server. Defaults to `1` - **WEBSITE**: Website to add to CORS configuration. _Required only if tunneled via CDN_ - **AUTO_THUMBNAIL**: Boolean flag to auto generate thumbnail images for preview. Defaults to `True` -- **SCAN_INTERVAL**: Interval in seconds to scan `VIDEO_SOURCE` in the background. Defaults traditional scan at run time. -> :bulb:   When `SCAN_INTERVAL` is set to `None`, `VIDEO_SOURCE` will be scanned in real time whenever the user -> loads the landing page. Setting a `SCAN_INTERVAL` will scan the `VIDEO_SOURCE` in background and reduce runtime -> delays in case of complex directory structure. ## Coding Standards Docstring format: [`Google`][google-docs]
          diff --git a/docs/_sources/README.md.txt b/docs/_sources/README.md.txt index 3ab0e8f..8a87603 100644 --- a/docs/_sources/README.md.txt +++ b/docs/_sources/README.md.txt @@ -15,9 +15,8 @@ Python module to, stream videos via authenticated sessions using FastAPI python -m pip install stream-localhost ``` -## Usage +## Sample Usage ```python -import asyncio import os import pystream @@ -29,7 +28,7 @@ if __name__ == '__main__': ) # Add the following to host on local IP address, skip for localhost (127.0.0.1) # kwargs["video_host"] = pystream.utils.get_local_ip() - asyncio.run(pystream.start(**kwargs)) + pystream.start(**kwargs) ``` ### Env Variables @@ -49,10 +48,6 @@ if __name__ == '__main__': - **WORKERS**: Number of workers to spin up the `uvicorn` server. Defaults to `1` - **WEBSITE**: Website to add to CORS configuration. _Required only if tunneled via CDN_ - **AUTO_THUMBNAIL**: Boolean flag to auto generate thumbnail images for preview. Defaults to `True` -- **SCAN_INTERVAL**: Interval in seconds to scan `VIDEO_SOURCE` in the background. Defaults traditional scan at run time. -> :bulb:   When `SCAN_INTERVAL` is set to `None`, `VIDEO_SOURCE` will be scanned in real time whenever the user -> loads the landing page. Setting a `SCAN_INTERVAL` will scan the `VIDEO_SOURCE` in background and reduce runtime -> delays in case of complex directory structure. ## Coding Standards Docstring format: [`Google`][google-docs]
          diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt index 154e6a4..9e405b6 100644 --- a/docs/_sources/index.rst.txt +++ b/docs/_sources/index.rst.txt @@ -42,13 +42,6 @@ Images :members: :undoc-members: -Scheduler -========= - -.. automodule:: pystream.models.scheduler - :members: - :undoc-members: - Squire ====== diff --git a/docs/genindex.html b/docs/genindex.html index b1b5aa8..9dbadfc 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -41,8 +41,7 @@

          Navigation

          Index

          - _ - | A + A | C | E | F @@ -61,14 +60,6 @@

          Index

          | W
          -

          _

          - - -
          -

          A

            @@ -79,10 +70,6 @@

            A

            C

            -
            • chunk_size (pystream.models.config.Static attribute)
            • @@ -180,14 +167,12 @@

              I

              L

              - +
              • - pystream.models.scheduler - -
              • -
              • pystream.models.squire
                  @@ -355,8 +331,6 @@

                  R

                -
              • RepeatedTimer (class in pystream.models.scheduler) -
              • root() (in module pystream.routers.basics)
              • RootFilter (class in pystream.logger) @@ -367,27 +341,17 @@

                R

                S

                - - -
                • startup_tasks() (in module pystream.main)
                • Static (class in pystream.models.config) -
                • -
                • stop() (pystream.models.scheduler.RepeatedTimer method)
                • stream_video() (in module pystream.routers.video)
                • diff --git a/docs/index.html b/docs/index.html index 3c27992..81f739a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -49,7 +49,7 @@

                  Stream-Localhost - A secured interface to stream videos
                • Video Streaming
                  • Install
                  • -
                  • Usage
                  • +
                  • Sample Usage
                  • Coding Standards
                  • Release Notes
                  • Linting
                  • @@ -71,7 +71,7 @@

                    Stream-Localhost - A secured interface to stream videos
                    -async pystream.main.start(**kwargs) None
                    +pystream.main.start(**kwargs) None

                    Starter function for the streaming API.

                    Parameters:
                    @@ -108,7 +108,7 @@

                    Models

                    Config

                    -class pystream.models.config.EnvConfig(_case_sensitive: bool | None = None, _env_prefix: str | None = None, _env_file: DotenvType | None = PosixPath('.'), _env_file_encoding: str | None = None, _env_nested_delimiter: str | None = None, _secrets_dir: str | Path | None = None, *, username: str, password: SecretStr, video_source: Path, video_host: IPv4Address = '127.0.0.1', video_port: int = 8000, file_formats: Sequence[str] = ('.mov', '.mp4'), workers: int = 1, website: Optional[Url] = None, auto_thumbnail: bool = True, scan_interval: Optional[int] = None)
                    +class pystream.models.config.EnvConfig(_case_sensitive: bool | None = None, _env_prefix: str | None = None, _env_file: DotenvType | None = PosixPath('.'), _env_file_encoding: str | None = None, _env_nested_delimiter: str | None = None, _secrets_dir: str | Path | None = None, *, username: str, password: SecretStr, video_source: Path, video_host: IPv4Address = '127.0.0.1', video_port: int = 8000, file_formats: Sequence[str] = ('.mov', '.mp4'), workers: int = 1, website: Optional[Url] = None, auto_thumbnail: bool = True)

                    Configure all env vars and validate using pydantic to share across modules.

                    >>> EnvConfig
                     
                    @@ -163,11 +163,6 @@

                    Modelsauto_thumbnail: bool

                    -
                    -
                    -scan_interval: Optional[int]
                    -
                    -
                    class Config
                    @@ -228,7 +223,7 @@

                    Models
                    -class pystream.models.config.Static(*, vault: str = 'stream', preview: str = 'preview', query_param: str = 'file', index_endpoint: str = '/index', logout_endpoint: str = '/logout', streaming_endpoint: str = '/video', chunk_size: int = 1048576, landing_page: Dict[str, List[Dict[str, str]]] = None)
                    +class pystream.models.config.Static(*, vault: str = 'stream', preview: str = 'preview', query_param: str = 'file', index_endpoint: str = '/index', logout_endpoint: str = '/logout', streaming_endpoint: str = '/video', chunk_size: int = 1048576)

                    Object to store static values.

                    >>> Static
                     
                    @@ -273,11 +268,6 @@

                    Modelschunk_size: int

                    -
                    -
                    -landing_page: Dict[str, List[Dict[str, str]]]
                    -
                    -

                    @@ -377,53 +367,6 @@

                    Models

                    - -
                    -

                    Scheduler

                    -
                    -
                    -class pystream.models.scheduler.RepeatedTimer(interval: int, function: Callable, args: Optional[Tuple] = None, kwargs: Optional[Dict[str, Any]] = None)
                    -

                    Instantiates RepeatedTimer object to kick off the threading.Timer object with custom intervals.

                    -
                    >>> RepeatedTimer
                    -
                    -
                    -

                    Repeats the Timer object from threading.

                    -
                    -
                    Parameters:
                    -
                      -
                    • interval – Interval in seconds.

                    • -
                    • function – Function to trigger with intervals.

                    • -
                    • args – Arguments for the function.

                    • -
                    • kwargs – Keyword arguments for the function.

                    • -
                    -
                    -
                    -
                    -
                    -_run()
                    -

                    Triggers the target function.

                    -
                    - -
                    -
                    -start()
                    -

                    Trigger target function if timer isn’t running already.

                    -
                    - -
                    -
                    -stop()
                    -

                    Stop the timer and cancel all futures.

                    -
                    - -
                    -
                    -cancel()
                    -

                    Initiate cancellation.

                    -
                    - -
                    -

                    Squire

                    @@ -508,12 +451,6 @@

                    Models -
                    -
                    -pystream.models.squire.scanner() None
                    -

                    Update the shared static object member to response from stream all content.

                    -
                    -
                    pystream.models.squire.remove_thumbnail(img_path: PosixPath) None
                    @@ -825,7 +762,6 @@

                    Table of Contents

                  • Authenticator
                  • Config
                  • Images
                  • -
                  • Scheduler
                  • Squire
                  • Stream
                  • Routers
                  • diff --git a/docs/objects.inv b/docs/objects.inv index daee170..b3f83da 100644 Binary files a/docs/objects.inv and b/docs/objects.inv differ diff --git a/docs/py-modindex.html b/docs/py-modindex.html index 4d5e2df..7dda66b 100644 --- a/docs/py-modindex.html +++ b/docs/py-modindex.html @@ -82,11 +82,6 @@

                    Python Module Index

                    pystream.models.images
                    - pystream.models.scheduler -
                    diff --git a/docs/searchindex.js b/docs/searchindex.js index 5d3a44b..8a87b3b 100644 --- a/docs/searchindex.js +++ b/docs/searchindex.js @@ -1 +1 @@ -Search.setIndex({"docnames": ["README", "index"], "filenames": ["README.md", "index.rst"], "titles": ["Video Streaming", "Stream-Localhost - A secured interface to stream videos"], "terms": {"deploy": 0, "python": 0, "modul": 0, "via": 0, "authent": 0, "session": [0, 1], "us": [0, 1], "fastapi": [0, 1], "m": 0, "pip": 0, "localhost": 0, "import": 0, "asyncio": 0, "o": 0, "pystream": [0, 1], "__name__": 0, "__main__": 0, "kwarg": [0, 1], "dict": [0, 1], "usernam": [0, 1], "foo": 0, "password": [0, 1], "bar": 0, "video_sourc": [0, 1], "path": [0, 1], "join": 0, "expandus": 0, "download": 0, "add": [0, 1], "follow": 0, "host": [0, 1], "local": [0, 1], "ip": [0, 1], "address": [0, 1], "skip": 0, "127": [0, 1], "0": [0, 1], "1": [0, 1], "video_host": [0, 1], "util": 0, "get_local_ip": [0, 1], "run": [0, 1], "start": [0, 1], "bulb": 0, "environ": [0, 1], "can": 0, "load": [0, 1], "from": [0, 1], "ani": [0, 1], "file": [0, 1], "default": 0, "set": 0, "var": [0, 1], "env_fil": [0, 1], "filenam": [0, 1], "custom": [0, 1], "mandatori": 0, "choic": 0, "_": 0, "sourc": [0, 1], "underscor": 0, "ignor": [0, 1], "option": [0, 1], "port": 0, "number": [0, 1], "applic": 0, "8000": [0, 1], "format": 0, "sequenc": [0, 1], "support": 0, "mp4": [0, 1], "mov": [0, 1], "worker": [0, 1], "spin": 0, "up": 0, "uvicorn": 0, "server": 0, "websit": [0, 1], "cor": 0, "configur": [0, 1], "requir": [0, 1], "onli": [0, 1], "tunnel": 0, "cdn": 0, "auto": 0, "thumbnail": [0, 1], "boolean": [0, 1], "flag": [0, 1], "gener": [0, 1], "imag": 0, "preview": [0, 1], "true": [0, 1], "scan": 0, "interv": [0, 1], "second": [0, 1], "background": 0, "tradit": 0, "time": [0, 1], "when": [0, 1], "scan_interv": [0, 1], "i": [0, 1], "none": [0, 1], "real": 0, "whenev": 0, "user": [0, 1], "land": 0, "page": [0, 1], "reduc": 0, "runtim": 0, "delai": 0, "case": 0, "complex": 0, "directori": [0, 1], "structur": 0, "docstr": 0, "googl": 0, "style": 0, "convent": 0, "pep": 0, "8": 0, "isort": 0, "gitvers": 0, "revers": 0, "f": 0, "release_not": 0, "rst": 0, "t": [0, 1], "pre": 0, "commit": 0, "ensur": 0, "pytest": 0, "valid": [0, 1], "hyperlink": 0, "all": [0, 1], "markdown": 0, "includ": [0, 1], "wiki": 0, "sphinx": 0, "5": 0, "recommonmark": 0, "http": [0, 1], "org": 0, "project": 0, "thevickypedia": 0, "github": 0, "io": 0, "vignesh": 0, "rao": 0, "under": 0, "mit": 0, "instal": 1, "usag": 1, "code": 1, "standard": 1, "releas": 1, "note": 1, "lint": 1, "pypi": 1, "packag": 1, "runbook": 1, "licens": 1, "copyright": 1, "startup_task": 1, "task": 1, "need": 1, "dure": 1, "api": 1, "startup": 1, "async": 1, "starter": 1, "function": 1, "paramet": 1, "keyword": 1, "argument": 1, "env": 1, "verifi": 1, "credenti": 1, "httpbasiccredenti": 1, "jsonrespons": 1, "client": 1, "return": 1, "json": 1, "respons": 1, "content": 1, "statu": 1, "type": 1, "class": 1, "envconfig": 1, "_case_sensit": 1, "bool": 1, "_env_prefix": 1, "str": 1, "_env_fil": 1, "dotenvtyp": 1, "posixpath": 1, "_env_file_encod": 1, "_env_nested_delimit": 1, "_secrets_dir": 1, "secretstr": 1, "ipv4address": 1, "video_port": 1, "int": 1, "file_format": 1, "url": 1, "auto_thumbnail": 1, "pydant": 1, "share": 1, "across": 1, "creat": 1, "new": 1, "pars": 1, "input": 1, "data": 1, "rais": 1, "validationerror": 1, "pydantic_cor": 1, "cannot": 1, "form": 1, "__init__": 1, "__pydantic_self__": 1, "instead": 1, "more": 1, "common": 1, "self": 1, "first": 1, "arg": 1, "allow": 1, "field": 1, "name": 1, "variabl": 1, "env_prefix": 1, "extra": 1, "hide_input_in_error": 1, "classmethod": 1, "parse_video_host": 1, "valu": 1, "string": 1, "notion": 1, "object": 1, "fileio": 1, "index": 1, "html": 1, "list_fil": 1, "static": 1, "vault": 1, "query_param": 1, "index_endpoint": 1, "logout_endpoint": 1, "logout": 1, "streaming_endpoint": 1, "chunk_siz": 1, "1048576": 1, "landing_pag": 1, "list": 1, "store": 1, "info": 1, "inform": 1, "alia": 1, "filepath": 1, "initi": 1, "captur": 1, "frame": 1, "particular": 1, "instanti": 1, "opencv": 1, "": 1, "videocaptur": 1, "generate_thumbnail": 1, "output_dir": 1, "output": 1, "success": 1, "failur": 1, "get_video_length": 1, "tupl": 1, "timedelta": 1, "get": 1, "calcul": 1, "length": 1, "datetim": 1, "generate_preview": 1, "at_second": 1, "which": 1, "should": 1, "repeatedtim": 1, "callabl": 1, "kick": 1, "off": 1, "thread": 1, "timer": 1, "repeat": 1, "trigger": 1, "_run": 1, "target": 1, "isn": 1, "alreadi": 1, "stop": 1, "cancel": 1, "futur": 1, "log_connect": 1, "request": 1, "log": 1, "connect": 1, "devic": 1, "thi": 1, "avoid": 1, "multipl": 1, "same": 1, "differ": 1, "natural_sort_kei": 1, "union": 1, "kei": 1, "sort": 1, "natur": 1, "wai": 1, "take": 1, "an": 1, "element": 1, "deriv": 1, "split": 1, "part": 1, "regular": 1, "express": 1, "get_dir_stream_cont": 1, "parent": 1, "subdir": 1, "insid": 1, "displai": 1, "login": 1, "subdirectori": 1, "within": 1, "exist": 1, "dictionari": 1, "pair": 1, "get_all_stream_cont": 1, "folder": 1, "contain": 1, "each": 1, "section": 1, "get_it": 1, "purepath": 1, "current": 1, "serv": 1, "previou": 1, "next": 1, "render": 1, "scanner": 1, "updat": 1, "member": 1, "remove_thumbnail": 1, "img_path": 1, "remov": 1, "send_bytes_range_request": 1, "file_obj": 1, "binaryio": 1, "start_rang": 1, "end_rang": 1, "asynciter": 1, "bytestr": 1, "send": 1, "chunk": 1, "rang": 1, "specif": 1, "rfc7233": 1, "byte": 1, "end": 1, "yield": 1, "iter": 1, "get_range_head": 1, "range_head": 1, "file_s": 1, "proce": 1, "header": 1, "size": 1, "range_requests_respons": 1, "file_path": 1, "streamingrespons": 1, "given": 1, "auth": 1, "depend": 1, "httpbasic": 1, "templaterespons": 1, "handler": 1, "templat": 1, "redirectrespons": 1, "401": 1, "out": 1, "httpexcept": 1, "messag": 1, "get_favicon": 1, "filerespons": 1, "favicon": 1, "ico": 1, "endpoint": 1, "robinhood": 1, "script": 1, "root": 1, "redirect": 1, "preview_load": 1, "ha": 1, "stream_video": 1, "video_path": 1, "video_endpoint": 1, "receiv": 1, "cooki": 1, "rootfilt": 1, "filter": 1, "while": 1, "preserv": 1, "other": 1, "access": 1, "200": 1, "ok": 1, "307": 1, "temporari": 1, "vid_nam": 1, "redund": 1, "pass": 1, "overrid": 1, "implement": 1, "subclass": 1, "The": 1, "method": 1, "record": 1, "examin": 1, "fals": 1, "discard": 1, "togeth": 1, "its": 1, "children": 1, "have": 1, "event": 1, "through": 1, "If": 1, "specifi": 1, "everi": 1, "logrecord": 1, "repres": 1, "someth": 1, "simpl": 1, "check": 1, "network": 1, "id": 1, "retriev": 1, "privat": 1, "machin": 1, "get_public_ip": 1, "extract": 1, "public": 1, "make": 1, "extern": 1, "search": 1}, "objects": {"pystream": [[1, 0, 0, "-", "logger"], [1, 0, 0, "-", "main"], [1, 0, 0, "-", "utils"]], "pystream.logger": [[1, 1, 1, "", "RootFilter"]], "pystream.logger.RootFilter": [[1, 2, 1, "", "filter"]], "pystream.main": [[1, 3, 1, "", "start"], [1, 3, 1, "", "startup_tasks"]], "pystream.models": [[1, 0, 0, "-", "authenticator"], [1, 0, 0, "-", "config"], [1, 0, 0, "-", "images"], [1, 0, 0, "-", "scheduler"], [1, 0, 0, "-", "squire"], [1, 0, 0, "-", "stream"]], "pystream.models.authenticator": [[1, 3, 1, "", "verify"]], "pystream.models.config": [[1, 1, 1, "", "EnvConfig"], [1, 1, 1, "", "FileIO"], [1, 1, 1, "", "Session"], [1, 1, 1, "", "Static"], [1, 4, 1, "", "env"]], "pystream.models.config.EnvConfig": [[1, 1, 1, "", "Config"], [1, 4, 1, "", "auto_thumbnail"], [1, 4, 1, "", "file_formats"], [1, 2, 1, "", "parse_video_host"], [1, 4, 1, "", "password"], [1, 4, 1, "", "scan_interval"], [1, 4, 1, "", "username"], [1, 4, 1, "", "video_host"], [1, 4, 1, "", "video_port"], [1, 4, 1, "", "video_source"], [1, 4, 1, "", "website"], [1, 4, 1, "", "workers"]], "pystream.models.config.EnvConfig.Config": [[1, 4, 1, "", "env_file"], [1, 4, 1, "", "env_prefix"], [1, 4, 1, "", "extra"], [1, 4, 1, "", "hide_input_in_errors"]], "pystream.models.config.FileIO": [[1, 4, 1, "", "index"], [1, 4, 1, "", "list_files"]], "pystream.models.config.Session": [[1, 4, 1, "", "info"]], "pystream.models.config.Static": [[1, 4, 1, "", "chunk_size"], [1, 4, 1, "", "index_endpoint"], [1, 4, 1, "", "landing_page"], [1, 4, 1, "", "logout_endpoint"], [1, 4, 1, "", "preview"], [1, 4, 1, "", "query_param"], [1, 4, 1, "", "streaming_endpoint"], [1, 4, 1, "", "vault"]], "pystream.models.images": [[1, 1, 1, "", "Images"]], "pystream.models.images.Images": [[1, 2, 1, "", "generate_preview"], [1, 2, 1, "", "generate_thumbnails"], [1, 2, 1, "", "get_video_length"]], "pystream.models.scheduler": [[1, 1, 1, "", "RepeatedTimer"]], "pystream.models.scheduler.RepeatedTimer": [[1, 2, 1, "", "_run"], [1, 2, 1, "", "cancel"], [1, 2, 1, "", "start"], [1, 2, 1, "", "stop"]], "pystream.models.squire": [[1, 3, 1, "", "get_all_stream_content"], [1, 3, 1, "", "get_dir_stream_content"], [1, 3, 1, "", "get_iter"], [1, 3, 1, "", "log_connection"], [1, 3, 1, "", "natural_sort_key"], [1, 3, 1, "", "remove_thumbnail"], [1, 3, 1, "", "scanner"]], "pystream.models.stream": [[1, 3, 1, "", "get_range_header"], [1, 3, 1, "", "range_requests_response"], [1, 3, 1, "", "send_bytes_range_requests"]], "pystream.routers": [[1, 0, 0, "-", "auth"], [1, 0, 0, "-", "basics"], [1, 0, 0, "-", "video"]], "pystream.routers.auth": [[1, 3, 1, "", "index"], [1, 3, 1, "", "logout"]], "pystream.routers.basics": [[1, 3, 1, "", "get_favicon"], [1, 3, 1, "", "root"]], "pystream.routers.video": [[1, 3, 1, "", "preview_loader"], [1, 3, 1, "", "stream_video"], [1, 3, 1, "", "video_endpoint"]], "pystream.utils": [[1, 3, 1, "", "get_local_ip"], [1, 3, 1, "", "get_public_ip"]]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method", "3": "py:function", "4": "py:attribute"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"], "3": ["py", "function", "Python function"], "4": ["py", "attribute", "Python attribute"]}, "titleterms": {"video": [0, 1], "stream": [0, 1], "instal": 0, "usag": 0, "env": 0, "variabl": 0, "code": 0, "standard": 0, "releas": 0, "note": 0, "lint": 0, "pypi": 0, "packag": 0, "runbook": 0, "licens": 0, "copyright": 0, "localhost": 1, "A": 1, "secur": 1, "interfac": 1, "read": 1, "me": 1, "main": 1, "modul": 1, "model": 1, "authent": 1, "config": 1, "imag": 1, "schedul": 1, "squir": 1, "router": 1, "basic": 1, "support": 1, "logger": 1, "util": 1, "indic": 1, "tabl": 1}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 6, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 56}}) \ No newline at end of file +Search.setIndex({"docnames": ["README", "index"], "filenames": ["README.md", "index.rst"], "titles": ["Video Streaming", "Stream-Localhost - A secured interface to stream videos"], "terms": {"deploy": 0, "python": 0, "modul": 0, "via": 0, "authent": 0, "session": [0, 1], "us": [0, 1], "fastapi": [0, 1], "m": 0, "pip": 0, "localhost": 0, "import": 0, "o": 0, "pystream": [0, 1], "__name__": 0, "__main__": 0, "kwarg": [0, 1], "dict": [0, 1], "usernam": [0, 1], "foo": 0, "password": [0, 1], "bar": 0, "video_sourc": [0, 1], "path": [0, 1], "join": 0, "expandus": 0, "download": 0, "add": [0, 1], "follow": 0, "host": [0, 1], "local": [0, 1], "ip": [0, 1], "address": [0, 1], "skip": 0, "127": [0, 1], "0": [0, 1], "1": [0, 1], "video_host": [0, 1], "util": 0, "get_local_ip": [0, 1], "start": [0, 1], "bulb": 0, "environ": [0, 1], "can": 0, "load": [0, 1], "from": [0, 1], "ani": 0, "file": [0, 1], "default": 0, "set": 0, "var": [0, 1], "env_fil": [0, 1], "filenam": [0, 1], "custom": 0, "mandatori": 0, "choic": 0, "_": 0, "sourc": [0, 1], "underscor": 0, "ignor": [0, 1], "option": [0, 1], "port": 0, "number": [0, 1], "applic": 0, "8000": [0, 1], "format": 0, "sequenc": [0, 1], "support": 0, "mp4": [0, 1], "mov": [0, 1], "worker": [0, 1], "spin": 0, "up": 0, "uvicorn": 0, "server": 0, "websit": [0, 1], "cor": 0, "configur": [0, 1], "requir": [0, 1], "onli": [0, 1], "tunnel": 0, "cdn": 0, "auto": 0, "thumbnail": [0, 1], "boolean": [0, 1], "flag": [0, 1], "gener": [0, 1], "imag": 0, "preview": [0, 1], "true": [0, 1], "docstr": 0, "googl": 0, "style": 0, "convent": 0, "pep": 0, "8": 0, "isort": 0, "gitvers": 0, "revers": 0, "f": 0, "release_not": 0, "rst": 0, "t": 0, "pre": 0, "commit": 0, "ensur": 0, "run": [0, 1], "pytest": 0, "valid": [0, 1], "hyperlink": 0, "all": [0, 1], "markdown": 0, "includ": [0, 1], "wiki": 0, "page": [0, 1], "sphinx": 0, "5": 0, "recommonmark": 0, "http": [0, 1], "org": 0, "project": 0, "thevickypedia": 0, "github": 0, "io": 0, "vignesh": 0, "rao": 0, "under": 0, "mit": 0, "instal": 1, "sampl": 1, "usag": 1, "code": 1, "standard": 1, "releas": 1, "note": 1, "lint": 1, "pypi": 1, "packag": 1, "runbook": 1, "licens": 1, "copyright": 1, "startup_task": 1, "none": 1, "task": 1, "need": 1, "dure": 1, "api": 1, "startup": 1, "starter": 1, "function": 1, "paramet": 1, "keyword": 1, "argument": 1, "env": 1, "async": 1, "verifi": 1, "credenti": 1, "httpbasiccredenti": 1, "jsonrespons": 1, "client": 1, "return": 1, "json": 1, "respons": 1, "content": 1, "statu": 1, "type": 1, "class": 1, "envconfig": 1, "_case_sensit": 1, "bool": 1, "_env_prefix": 1, "str": 1, "_env_fil": 1, "dotenvtyp": 1, "posixpath": 1, "_env_file_encod": 1, "_env_nested_delimit": 1, "_secrets_dir": 1, "secretstr": 1, "ipv4address": 1, "video_port": 1, "int": 1, "file_format": 1, "url": 1, "auto_thumbnail": 1, "pydant": 1, "share": 1, "across": 1, "creat": 1, "new": 1, "pars": 1, "input": 1, "data": 1, "rais": 1, "validationerror": 1, "pydantic_cor": 1, "cannot": 1, "form": 1, "__init__": 1, "__pydantic_self__": 1, "instead": 1, "more": 1, "common": 1, "self": 1, "first": 1, "arg": 1, "allow": 1, "field": 1, "name": 1, "variabl": 1, "env_prefix": 1, "extra": 1, "hide_input_in_error": 1, "classmethod": 1, "parse_video_host": 1, "valu": 1, "string": 1, "notion": 1, "object": 1, "fileio": 1, "index": 1, "html": 1, "list_fil": 1, "static": 1, "vault": 1, "query_param": 1, "index_endpoint": 1, "logout_endpoint": 1, "logout": 1, "streaming_endpoint": 1, "chunk_siz": 1, "1048576": 1, "store": 1, "info": 1, "inform": 1, "alia": 1, "filepath": 1, "initi": 1, "captur": 1, "frame": 1, "particular": 1, "instanti": 1, "opencv": 1, "": 1, "videocaptur": 1, "generate_thumbnail": 1, "interv": 1, "output_dir": 1, "second": 1, "output": 1, "directori": 1, "success": 1, "failur": 1, "get_video_length": 1, "tupl": 1, "timedelta": 1, "get": 1, "calcul": 1, "length": 1, "datetim": 1, "generate_preview": 1, "at_second": 1, "time": 1, "which": 1, "should": 1, "log_connect": 1, "request": 1, "log": 1, "connect": 1, "devic": 1, "thi": 1, "avoid": 1, "multipl": 1, "when": 1, "same": 1, "differ": 1, "natural_sort_kei": 1, "list": 1, "union": 1, "kei": 1, "sort": 1, "natur": 1, "wai": 1, "take": 1, "an": 1, "element": 1, "deriv": 1, "split": 1, "part": 1, "regular": 1, "express": 1, "get_dir_stream_cont": 1, "parent": 1, "subdir": 1, "insid": 1, "displai": 1, "login": 1, "subdirectori": 1, "within": 1, "exist": 1, "dictionari": 1, "pair": 1, "get_all_stream_cont": 1, "folder": 1, "contain": 1, "each": 1, "section": 1, "get_it": 1, "purepath": 1, "current": 1, "serv": 1, "previou": 1, "next": 1, "render": 1, "remove_thumbnail": 1, "img_path": 1, "trigger": 1, "timer": 1, "remov": 1, "send_bytes_range_request": 1, "file_obj": 1, "binaryio": 1, "start_rang": 1, "end_rang": 1, "asynciter": 1, "bytestr": 1, "send": 1, "chunk": 1, "rang": 1, "specif": 1, "rfc7233": 1, "byte": 1, "end": 1, "yield": 1, "iter": 1, "get_range_head": 1, "range_head": 1, "file_s": 1, "proce": 1, "header": 1, "size": 1, "range_requests_respons": 1, "file_path": 1, "streamingrespons": 1, "given": 1, "auth": 1, "depend": 1, "httpbasic": 1, "templaterespons": 1, "handler": 1, "templat": 1, "redirectrespons": 1, "401": 1, "out": 1, "user": 1, "httpexcept": 1, "messag": 1, "get_favicon": 1, "filerespons": 1, "favicon": 1, "ico": 1, "endpoint": 1, "robinhood": 1, "script": 1, "root": 1, "redirect": 1, "preview_load": 1, "ha": 1, "stream_video": 1, "video_path": 1, "video_endpoint": 1, "receiv": 1, "cooki": 1, "rootfilt": 1, "filter": 1, "while": 1, "preserv": 1, "other": 1, "access": 1, "200": 1, "ok": 1, "307": 1, "temporari": 1, "vid_nam": 1, "redund": 1, "pass": 1, "overrid": 1, "implement": 1, "subclass": 1, "The": 1, "method": 1, "record": 1, "examin": 1, "fals": 1, "discard": 1, "togeth": 1, "its": 1, "children": 1, "have": 1, "event": 1, "through": 1, "If": 1, "i": 1, "specifi": 1, "everi": 1, "logrecord": 1, "repres": 1, "someth": 1, "simpl": 1, "check": 1, "network": 1, "id": 1, "retriev": 1, "privat": 1, "machin": 1, "get_public_ip": 1, "extract": 1, "public": 1, "make": 1, "extern": 1, "search": 1}, "objects": {"pystream": [[1, 0, 0, "-", "logger"], [1, 0, 0, "-", "main"], [1, 0, 0, "-", "utils"]], "pystream.logger": [[1, 1, 1, "", "RootFilter"]], "pystream.logger.RootFilter": [[1, 2, 1, "", "filter"]], "pystream.main": [[1, 3, 1, "", "start"], [1, 3, 1, "", "startup_tasks"]], "pystream.models": [[1, 0, 0, "-", "authenticator"], [1, 0, 0, "-", "config"], [1, 0, 0, "-", "images"], [1, 0, 0, "-", "squire"], [1, 0, 0, "-", "stream"]], "pystream.models.authenticator": [[1, 3, 1, "", "verify"]], "pystream.models.config": [[1, 1, 1, "", "EnvConfig"], [1, 1, 1, "", "FileIO"], [1, 1, 1, "", "Session"], [1, 1, 1, "", "Static"], [1, 4, 1, "", "env"]], "pystream.models.config.EnvConfig": [[1, 1, 1, "", "Config"], [1, 4, 1, "", "auto_thumbnail"], [1, 4, 1, "", "file_formats"], [1, 2, 1, "", "parse_video_host"], [1, 4, 1, "", "password"], [1, 4, 1, "", "username"], [1, 4, 1, "", "video_host"], [1, 4, 1, "", "video_port"], [1, 4, 1, "", "video_source"], [1, 4, 1, "", "website"], [1, 4, 1, "", "workers"]], "pystream.models.config.EnvConfig.Config": [[1, 4, 1, "", "env_file"], [1, 4, 1, "", "env_prefix"], [1, 4, 1, "", "extra"], [1, 4, 1, "", "hide_input_in_errors"]], "pystream.models.config.FileIO": [[1, 4, 1, "", "index"], [1, 4, 1, "", "list_files"]], "pystream.models.config.Session": [[1, 4, 1, "", "info"]], "pystream.models.config.Static": [[1, 4, 1, "", "chunk_size"], [1, 4, 1, "", "index_endpoint"], [1, 4, 1, "", "logout_endpoint"], [1, 4, 1, "", "preview"], [1, 4, 1, "", "query_param"], [1, 4, 1, "", "streaming_endpoint"], [1, 4, 1, "", "vault"]], "pystream.models.images": [[1, 1, 1, "", "Images"]], "pystream.models.images.Images": [[1, 2, 1, "", "generate_preview"], [1, 2, 1, "", "generate_thumbnails"], [1, 2, 1, "", "get_video_length"]], "pystream.models.squire": [[1, 3, 1, "", "get_all_stream_content"], [1, 3, 1, "", "get_dir_stream_content"], [1, 3, 1, "", "get_iter"], [1, 3, 1, "", "log_connection"], [1, 3, 1, "", "natural_sort_key"], [1, 3, 1, "", "remove_thumbnail"]], "pystream.models.stream": [[1, 3, 1, "", "get_range_header"], [1, 3, 1, "", "range_requests_response"], [1, 3, 1, "", "send_bytes_range_requests"]], "pystream.routers": [[1, 0, 0, "-", "auth"], [1, 0, 0, "-", "basics"], [1, 0, 0, "-", "video"]], "pystream.routers.auth": [[1, 3, 1, "", "index"], [1, 3, 1, "", "logout"]], "pystream.routers.basics": [[1, 3, 1, "", "get_favicon"], [1, 3, 1, "", "root"]], "pystream.routers.video": [[1, 3, 1, "", "preview_loader"], [1, 3, 1, "", "stream_video"], [1, 3, 1, "", "video_endpoint"]], "pystream.utils": [[1, 3, 1, "", "get_local_ip"], [1, 3, 1, "", "get_public_ip"]]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method", "3": "py:function", "4": "py:attribute"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"], "3": ["py", "function", "Python function"], "4": ["py", "attribute", "Python attribute"]}, "titleterms": {"video": [0, 1], "stream": [0, 1], "instal": 0, "sampl": 0, "usag": 0, "env": 0, "variabl": 0, "code": 0, "standard": 0, "releas": 0, "note": 0, "lint": 0, "pypi": 0, "packag": 0, "runbook": 0, "licens": 0, "copyright": 0, "localhost": 1, "A": 1, "secur": 1, "interfac": 1, "read": 1, "me": 1, "main": 1, "modul": 1, "model": 1, "authent": 1, "config": 1, "imag": 1, "squir": 1, "router": 1, "basic": 1, "support": 1, "logger": 1, "util": 1, "indic": 1, "tabl": 1}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 6, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 56}}) \ No newline at end of file diff --git a/pystream/main.py b/pystream/main.py index 39e0b48..c370293 100644 --- a/pystream/main.py +++ b/pystream/main.py @@ -3,7 +3,7 @@ from fastapi.middleware.cors import CORSMiddleware from pystream.logger import logger -from pystream.models import config, scheduler, squire +from pystream.models import config from pystream.routers import auth, basics, video app = FastAPI() @@ -26,10 +26,9 @@ def startup_tasks() -> None: ]) kwargs = dict(allow_origins=origins) app.add_middleware(CORSMiddleware, **kwargs) - squire.scanner() -async def start(**kwargs) -> None: +def start(**kwargs) -> None: """Starter function for the streaming API. Args: @@ -54,19 +53,7 @@ async def start(**kwargs) -> None: "log_config": log_config, "workers": config.env.workers } - uvicorn_config = uvicorn.Config(**argument_dict) - uvicorn_server = uvicorn.Server(config=uvicorn_config) # Run startup tasks startup_tasks() - if config.env.scan_interval: - # Initiate background task with repeated timer - logger.info("'%s' will be scanned every %s seconds", config.env.video_source, config.env.scan_interval) - background_task = scheduler.RepeatedTimer(function=squire.scanner, interval=config.env.scan_interval) - background_task.start() # Start background task - else: - logger.info("'%s' will be scanned in realtime", config.env.video_source) - background_task = None - await uvicorn_server.serve() # Await uvicorn server - if background_task: - background_task.stop() # Stop background task when server process has finished + uvicorn.run(**argument_dict) diff --git a/pystream/models/config.py b/pystream/models/config.py index 0b74297..54bcae1 100644 --- a/pystream/models/config.py +++ b/pystream/models/config.py @@ -1,7 +1,7 @@ import os import socket from ipaddress import IPv4Address -from typing import Dict, List, Sequence, Union +from typing import Sequence, Union from pydantic import (BaseModel, DirectoryPath, Field, HttpUrl, PositiveInt, SecretStr, field_validator) @@ -26,7 +26,6 @@ class EnvConfig(BaseSettings): workers: int = Field(1, le=os.cpu_count(), ge=1, env="WORKERS") website: Union[HttpUrl, None] = None auto_thumbnail: bool = True - scan_interval: Union[PositiveInt, None] = Field(None, ge=30, le=86_400) # range: 30s to 24h class Config: """Environment variables configuration.""" @@ -68,7 +67,6 @@ class Static(BaseModel): logout_endpoint: str = "/logout" streaming_endpoint: str = "/video" chunk_size: PositiveInt = 1024 * 1024 - landing_page: Dict[str, List[Dict[str, str]]] = None class Session(BaseModel): diff --git a/pystream/models/scheduler.py b/pystream/models/scheduler.py deleted file mode 100644 index dcdf4c0..0000000 --- a/pystream/models/scheduler.py +++ /dev/null @@ -1,54 +0,0 @@ -import threading -from typing import Any, Callable, Dict, Tuple - - -class RepeatedTimer: - """Instantiates RepeatedTimer object to kick off the threading.Timer object with custom intervals. - - >>> RepeatedTimer - - """ - - def __init__( - self, - interval: int, - function: Callable, - args: Tuple = None, - kwargs: Dict[str, Any] = None, - ): - """Repeats the ``Timer`` object from threading. - - Args: - interval: Interval in seconds. - function: Function to trigger with intervals. - args: Arguments for the function. - kwargs: Keyword arguments for the function. - """ - self._timer = None - self.interval = interval - self.function = function - self.args = args or () - self.kwargs = kwargs or {} - self.is_running = False - - def _run(self): - """Triggers the target function.""" - self.is_running = False - self.start() - self.function(*self.args, **self.kwargs) - - def start(self): - """Trigger target function if timer isn't running already.""" - if not self.is_running: - self._timer = threading.Timer(self.interval, self._run) - self._timer.start() - self.is_running = True - - def stop(self): - """Stop the timer and cancel all futures.""" - self._timer.cancel() - self.is_running = False - - def cancel(self): - """Initiate cancellation.""" - self.stop() diff --git a/pystream/models/squire.py b/pystream/models/squire.py index 4e2d1d9..41ec0de 100644 --- a/pystream/models/squire.py +++ b/pystream/models/squire.py @@ -112,11 +112,6 @@ def get_iter(filename: pathlib.PurePath) -> Union[Tuple[str, str], Tuple[None, N return None, None -def scanner() -> None: - """Update the shared static object member to response from stream all content.""" - config.static.landing_page = get_all_stream_content() - - def remove_thumbnail(img_path: pathlib.PosixPath) -> None: """Triggered by timer to remove the thumbnail file. diff --git a/pystream/routers/auth.py b/pystream/routers/auth.py index d099dd9..66dd845 100644 --- a/pystream/routers/auth.py +++ b/pystream/routers/auth.py @@ -29,11 +29,11 @@ async def index(request: Request, """ await authenticator.verify(credentials) squire.log_connection(request) - config.env.scan_interval or squire.scanner() + landing_page = squire.get_all_stream_content() return templates.TemplateResponse( name=config.fileio.list_files, context={"request": request, "logout": config.static.logout_endpoint, - "files": config.static.landing_page['files'], "directories": config.static.landing_page['directories']} + "files": landing_page['files'], "directories": landing_page['directories']} ) diff --git a/pystream/routers/video.py b/pystream/routers/video.py index 9a53bc2..1a201f3 100644 --- a/pystream/routers/video.py +++ b/pystream/routers/video.py @@ -75,6 +75,7 @@ async def stream_video(request: Request, ) if pure_path.exists(): prev_, next_ = squire.get_iter(pure_path) + # fixme: perhaps, remove previous and next buttons in template instead of defaulting to # attrs = { "request": request, "title": video_path, "path": f"{config.static.streaming_endpoint}?{config.static.query_param}={urlparse.quote(str(pure_path))}",