From eb2152e7e6b1c0cf1318ec3bc271ad74850daa60 Mon Sep 17 00:00:00 2001 From: thevickypedia Date: Mon, 4 Dec 2023 12:26:55 -0600 Subject: [PATCH] Set password as `SecretStr` Remove redundant logging filters Hide inputs in pydantic's validation errors --- .pre-commit-config.yaml | 24 ++--- doc_gen/index.rst | 21 ++-- docs/_sources/index.rst.txt | 21 ++-- docs/genindex.html | 50 ++++++---- docs/index.html | 162 ++++++++++++++++--------------- docs/objects.inv | Bin 752 -> 781 bytes docs/py-modindex.html | 13 ++- docs/searchindex.js | 2 +- pystream/logger.py | 35 +++++-- pystream/models/authenticator.py | 2 +- pystream/models/config.py | 6 +- pystream/models/filters.py | 47 --------- release_notes.rst | 4 + 13 files changed, 203 insertions(+), 184 deletions(-) delete mode 100644 pystream/models/filters.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4102250..d0de4bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,6 @@ fail_fast: true exclude: ^docs repos: - - repo: https://github.com/PyCQA/flake8 - rev: '6.1.0' - hooks: - - id: flake8 - additional_dependencies: - - flake8-docstrings - - flake8-sfs - # SFS101: String literal formatting using percent operator. - # SFS201: String literal formatting using .format method. - # SFS301: String literal formatting using f-string. - args: [--max-line-length=120, --extend-ignore=SFS3 D107 D100 D104 D401 D412 SFS101 SFS201] - - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: @@ -39,6 +27,18 @@ repos: - id: requirements-txt-fixer - id: trailing-whitespace + - repo: https://github.com/PyCQA/flake8 + rev: '6.1.0' + hooks: + - id: flake8 + additional_dependencies: + - flake8-docstrings + - flake8-sfs + # SFS101: String literal formatting using percent operator. + # SFS201: String literal formatting using .format method. + # SFS301: String literal formatting using f-string. + args: [--max-line-length=120, --extend-ignore=SFS3 D107 D100 D104 D401 D412 SFS101 SFS201] + - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: diff --git a/doc_gen/index.rst b/doc_gen/index.rst index eae7180..468772f 100644 --- a/doc_gen/index.rst +++ b/doc_gen/index.rst @@ -19,6 +19,20 @@ Main Module :members: :undoc-members: +Logger +====== + +.. automodule:: pystream.logger + :members: + :undoc-members: + +Utils +===== + +.. automodule:: pystream.utils + :members: + :undoc-members: + Models ====== Authenticator @@ -35,13 +49,6 @@ Config :members: :exclude-members: _abc_impl, model_config, model_fields -Filters -======= - -.. automodule:: pystream.models.filters - :members: - :undoc-members: - Ngrok ===== diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt index eae7180..468772f 100644 --- a/docs/_sources/index.rst.txt +++ b/docs/_sources/index.rst.txt @@ -19,6 +19,20 @@ Main Module :members: :undoc-members: +Logger +====== + +.. automodule:: pystream.logger + :members: + :undoc-members: + +Utils +===== + +.. automodule:: pystream.utils + :members: + :undoc-members: + Models ====== Authenticator @@ -35,13 +49,6 @@ Config :members: :exclude-members: _abc_impl, model_config, model_fields -Filters -======= - -.. automodule:: pystream.models.filters - :members: - :undoc-members: - Ngrok ===== diff --git a/docs/genindex.html b/docs/genindex.html index 0e4c863..5cae456 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -45,6 +45,7 @@

Index

| E | F | G + | H | I | L | M @@ -92,12 +93,8 @@

F

@@ -107,9 +104,13 @@

G

  • get_dir_content() (in module pystream.models.squire)
  • get_favicon() (in module pystream.routers.basics) +
  • +
  • get_local_ip() (in module pystream.utils)
  • +

    H

    + + +
    +

    I

    @@ -192,6 +203,13 @@

    P

  • password (pystream.models.config.EnvConfig attribute)
  • + pystream.logger + +
  • +
  • pystream.main @@ -275,7 +293,7 @@

    R

  • diff --git a/docs/index.html b/docs/index.html index baa1264..3af8209 100644 --- a/docs/index.html +++ b/docs/index.html @@ -80,6 +80,79 @@

    Stream-Localhost - A secured interface to stream videos + +
    +

    Logger

    +
    +
    +class pystream.logger.RootFilter(name='')
    +

    Class to initiate / filter in logs while preserving other access logs.

    +
    >>> RootFilter
    +
    +
    +
    +

    See also

    +
      +
    • Filters "GET /login HTTP/1.1" 200 OK, "GET / HTTP/1.1" 307 Temporary Redirect, /video?vid_name=...

    • +
    • Includes redundant logging for each iterable passed in StreamingResponse

    • +
    • Overrides logging by implementing a subclass of logging.Filter

    • +
    • The method filter(record), that examines the log record and returns True to log it or False to discard it.

    • +
    +
    +

    Initialize a filter.

    +

    Initialize with the name of the logger which, together with its +children, will have its events allowed through the filter. If no +name is specified, allow every event.

    +
    +
    +filter(record: LogRecord) bool
    +

    Filter out logging at / from log streams.

    +
    +
    Parameters:
    +

    recordLogRecord represents an event which is created every time something is logged.

    +
    +
    Returns:
    +

    False flag for the endpoint that needs to be filtered.

    +
    +
    Return type:
    +

    bool

    +
    +
    +
    + +
    + +
    +
    +

    Utils

    +
    +
    +pystream.utils.get_local_ip() IPv4Address
    +

    Uses simple check on network id to retrieve the local IP address.

    +
    +
    Returns:
    +

    Private IP address of host machine.

    +
    +
    Return type:
    +

    IPv4Address

    +
    +
    +
    + +
    +
    +pystream.utils.get_public_ip() IPv4Address
    +

    Extract public IP address of the connection by making a request to external source.

    +
    +
    Returns:
    +

    Public IP address of host machine’s connection.

    +
    +
    Return type:
    +

    IPv4Address

    +
    +
    +
    +

    Models

    @@ -108,7 +181,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: str, video_source: Path, video_port: int = 8000, website: Optional[Url] = None, ngrok_token: Optional[str] = None, video_host: IPv4Address = '127.0.0.1', workers: int = 1)
    +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_port: int = 8000, website: Optional[Url] = None, ngrok_token: Optional[str] = None, video_host: IPv4Address = '127.0.0.1', workers: int = 1)

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

    >>> EnvConfig
     
    @@ -125,7 +198,7 @@

    Models
    -password: str
    +password: SecretStr

    @@ -177,6 +250,11 @@

    Modelsextra = 'ignore'

    +
    +
    +hide_input_in_errors = True
    +
    + @@ -254,83 +332,6 @@

    Models

    alias of EnvConfig

    -

    -
    -

    Filters

    -
    -
    -class pystream.models.filters.VideoFilter(name='')
    -

    Class to initiate /video filter in logs while preserving other access logs.

    -
    >>> VideoFilter
    -
    -
    -
    -

    See also

    -
      -
    • Overrides logging by implementing a subclass of logging.Filter

    • -
    • The method filter(record), that examines the log record and returns True to log it or False to discard it.

    • -
    -
    -

    Initialize a filter.

    -

    Initialize with the name of the logger which, together with its -children, will have its events allowed through the filter. If no -name is specified, allow every event.

    -
    -
    -filter(record: LogRecord) bool
    -

    Filter out logging at /video from log streams.

    -
    -
    Parameters:
    -

    recordLogRecord represents an event which is created every time something is logged.

    -
    -
    Returns:
    -

    False flag for the endpoint that needs to be filtered.

    -
    -
    Return type:
    -

    bool

    -
    -
    -
    - -
    - -
    -
    -class pystream.models.filters.RootFilter(name='')
    -

    Class to initiate / filter in logs while preserving other access logs.

    -
    >>> RootFilter
    -
    -
    -
    -

    See also

    -
      -
    • Overrides logging by implementing a subclass of logging.Filter

    • -
    • The method filter(record), that examines the log record and returns True to log it or False to discard it.

    • -
    -
    -

    Initialize a filter.

    -

    Initialize with the name of the logger which, together with its -children, will have its events allowed through the filter. If no -name is specified, allow every event.

    -
    -
    -filter(record: LogRecord) bool
    -

    Filter out logging at / from log streams.

    -
    -
    Parameters:
    -

    recordLogRecord represents an event which is created every time something is logged.

    -
    -
    Returns:
    -

    False flag for the endpoint that needs to be filtered.

    -
    -
    Return type:
    -

    bool

    -
    -
    -
    - -
    -

    Ngrok

    @@ -608,10 +609,11 @@

    Table of Contents

    • Stream-Localhost - A secured interface to stream videos
    • Main Module
    • +
    • Logger
    • +
    • Utils
    • Models
    • Authenticator
    • Config
    • -
    • Filters
    • Ngrok
    • Squire
    • Stream
    • diff --git a/docs/objects.inv b/docs/objects.inv index abbd7f1f21321ecde20830807274735a5232931a..0e50804bdd1b208d93a3c79017a781dc3e542b50 100644 GIT binary patch delta 676 zcmV;V0$cs?1&s!fb$^#lZ`&{ohVS_m2DWR2_1as}WmvIxE1I>3T?ECZZ6S{6id35X z`^i$;B|z6iaxQ@_@p<_nnNs>Ku?4jf9X1WvqTgPwuyP#~UkYt1*oc-_-KRJ<{aj2Z z{UIS95c!+#IKpM8h>DAS_G3uYZ|n-V#p(lirL;+jaIhD|%75}e)y|Zx(HwCQ0oO}b zw4_r(te_*I9JgRtmx?i|mhbUm4OZ9NvNo;x)N1sSu77>GP_{^X9;F`bnUn~fEl20~0t?bSUIy(FayOFi&~WoB{X34F2*zVP zi%t`V29TM+9EgUtnVi86^eNPs>6(@A;~Aa58H$J^g|3cgt; zf}z`F96f;(4X|Wq40LHHUZkxvSu{O6Z6PD*2!^ODZGWXN8vBzw$9d3*6QgAO)oS1k zL@Z4ryycW3z6~i@Wg3tzs48CPQoV~PVBy&TG5H7T(GX;55tvFYwhSZ>WlvPI=(oI$ zH?own>~q74Xp`Y^0R3wsxlp7_$_X}-VG(P!)+L%O*Zvh#gAPY#PRs$^t+sqqN%g!t zvi)Z1QGcKO-8t4>QlSG7>f2$dI;r~>Uy8e(V2DnnRjE*|I$Zc?BvCs6 delta 646 zcmV;10(t$72Ji)tb$^ykQ`;~QhVS_mnrW}mlq-nL)j-1MuMPP#*T zNvtgQRP9X38jUzR;CX%UoYYTabG zj-Z^I-NlPU*(#!AeZHddR0S;t5=R;oLpz2)=#Grd}6D{YMI93reNHF z87tNKYCoc_#_Bi%Yt(jh-FLbIEY?VT8l?{HiIfPPEl20~2rJS&UIy(3Eak~u`unl? zoqpLOCxh`A&wsMj#Nn1^Ca?se{%uRnU;upzHDzCn_g6SOgabMwhVWWv zR4;I{0WR1X18v%GJ<`^hESesiwvZ8Y1Vhx7wuL`*?0?&n9OpqJE{u|CvFUjO5lfQ@ zqnt9tQJ;cUrh#G&s*1-Z)w_%W79JcBlmDO&4MCO`fvJkso`H(DvPY^}bm2zyZD&eZ z4!M3uw8?PTgMOGut`zB#a)OOy*u+}xbcrU*wI5=bn4XC^6D&k{JH;|M(WaR+U}6K&+qY`vUnWmH&qij^e@Q4LJ~0TK;P+jZ?lGynhq diff --git a/docs/py-modindex.html b/docs/py-modindex.html index fe9ce77..d9a9aed 100644 --- a/docs/py-modindex.html +++ b/docs/py-modindex.html @@ -60,22 +60,22 @@

      Python Module Index

          - pystream.main + pystream.logger     - pystream.models.authenticator + pystream.main     - pystream.models.config + pystream.models.authenticator     - pystream.models.filters + pystream.models.config @@ -107,6 +107,11 @@

      Python Module Index

          pystream.routers.video + + +     + pystream.utils + diff --git a/docs/searchindex.js b/docs/searchindex.js index bf34ced..b3ecfc4 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, "pystream": [0, 1], "__name__": 0, "__main__": 0, "start": [0, 1], "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], "mandatori": 0, "usernam": [0, 1], "choic": 0, "password": [0, 1], "_": 0, "sourc": 0, "path": [0, 1], "mp4": 0, "option": [0, 1], "host": 0, "ip": 0, "address": 0, "127": [0, 1], "0": [0, 1], "1": [0, 1], "port": [0, 1], "number": 0, "applic": 0, "8000": [0, 1], "websit": [0, 1], "add": [0, 1], "cor": 0, "configur": [0, 1], "worker": [0, 1], "spin": 0, "up": 0, "uvicorn": [0, 1], "server": 0, "docstr": 0, "format": 0, "googl": 0, "style": 0, "convent": 0, "pep": 0, "8": 0, "isort": 0, "requir": [0, 1], "gitvers": 0, "revers": [0, 1], "f": 0, "release_not": 0, "rst": 0, "t": 0, "pre": 0, "commit": 0, "ensur": 0, "run": [0, 1], "pytest": 0, "gener": 0, "valid": [0, 1], "hyperlink": 0, "all": [0, 1], "markdown": 0, "includ": 0, "wiki": 0, "page": [0, 1], "sphinx": 0, "5": 0, "recommonmark": 0, "http": 0, "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, "none": 1, "task": 1, "need": 1, "dure": 1, "api": 1, "startup": 1, "kwarg": 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, "video_sourc": 1, "video_port": 1, "int": 1, "url": 1, "ngrok_token": 1, "video_host": 1, "ipv4address": 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, "ignor": 1, "fileio": 1, "index": 1, "html": 1, "list_fil": 1, "static": 1, "chunk_siz": 1, "1048576": 1, "vault": 1, "object": 1, "store": 1, "valu": 1, "info": 1, "dict": 1, "inform": 1, "alia": 1, "videofilt": 1, "initi": 1, "log": 1, "while": 1, "preserv": 1, "other": 1, "access": 1, "overrid": 1, "implement": 1, "subclass": 1, "The": 1, "method": 1, "record": 1, "examin": 1, "true": 1, "fals": 1, "discard": 1, "logger": 1, "which": 1, "togeth": 1, "its": 1, "children": 1, "have": 1, "event": 1, "through": 1, "If": 1, "i": 1, "specifi": 1, "everi": 1, "logrecord": 1, "out": 1, "repres": 1, "an": 1, "time": 1, "someth": 1, "flag": 1, "endpoint": 1, "rootfilt": 1, "get_tunnel": 1, "union": 1, "noreturn": 1, "check": 1, "activ": 1, "public": 1, "tunnel": 1, "httpurl": 1, "run_tunnel": 1, "proxi": 1, "expos": 1, "take": 1, "custom": 1, "log_connect": 1, "request": 1, "connect": 1, "onli": 1, "devic": 1, "thi": 1, "avoid": 1, "multipl": 1, "when": 1, "same": 1, "differ": 1, "get_dir_cont": 1, "parent": 1, "subdir": 1, "list": 1, "get": 1, "insid": 1, "particular": 1, "directori": 1, "displai": 1, "login": 1, "subdirectori": 1, "within": 1, "exist": 1, "dictionari": 1, "filepath": 1, "kei": 1, "pair": 1, "get_stream_cont": 1, "folder": 1, "contain": 1, "each": 1, "section": 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, "tupl": 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, "logout": 1, "401": 1, "user": 1, "httpexcept": 1, "messag": 1, "get_favicon": 1, "filerespons": 1, "favicon": 1, "ico": 1, "support": 1, "robinhood": 1, "script": 1, "": 1, "root": 1, "redirectrespons": 1, "render": 1, "redirect": 1, "stream_video": 1, "video_path": 1, "ha": 1, "video_endpoint": 1, "receiv": 1, "cooki": 1, "search": 1}, "objects": {"pystream": [[1, 0, 0, "-", "main"]], "pystream.main": [[1, 1, 1, "", "start"], [1, 1, 1, "", "startup_tasks"]], "pystream.models": [[1, 0, 0, "-", "authenticator"], [1, 0, 0, "-", "config"], [1, 0, 0, "-", "filters"], [1, 0, 0, "-", "ngrok"], [1, 0, 0, "-", "squire"], [1, 0, 0, "-", "stream"]], "pystream.models.authenticator": [[1, 1, 1, "", "verify"]], "pystream.models.config": [[1, 2, 1, "", "EnvConfig"], [1, 2, 1, "", "FileIO"], [1, 2, 1, "", "Session"], [1, 2, 1, "", "Static"], [1, 3, 1, "", "env"]], "pystream.models.config.EnvConfig": [[1, 2, 1, "", "Config"], [1, 3, 1, "", "ngrok_token"], [1, 3, 1, "", "password"], [1, 3, 1, "", "username"], [1, 3, 1, "", "video_host"], [1, 3, 1, "", "video_port"], [1, 3, 1, "", "video_source"], [1, 3, 1, "", "website"], [1, 3, 1, "", "workers"]], "pystream.models.config.EnvConfig.Config": [[1, 3, 1, "", "env_file"], [1, 3, 1, "", "env_prefix"], [1, 3, 1, "", "extra"]], "pystream.models.config.FileIO": [[1, 3, 1, "", "index"], [1, 3, 1, "", "list_files"]], "pystream.models.config.Session": [[1, 3, 1, "", "info"]], "pystream.models.config.Static": [[1, 3, 1, "", "CHUNK_SIZE"], [1, 3, 1, "", "VAULT"]], "pystream.models.filters": [[1, 2, 1, "", "RootFilter"], [1, 2, 1, "", "VideoFilter"]], "pystream.models.filters.RootFilter": [[1, 4, 1, "", "filter"]], "pystream.models.filters.VideoFilter": [[1, 4, 1, "", "filter"]], "pystream.models.ngrok": [[1, 1, 1, "", "get_tunnel"], [1, 1, 1, "", "run_tunnel"]], "pystream.models.squire": [[1, 1, 1, "", "get_dir_content"], [1, 1, 1, "", "get_stream_content"], [1, 1, 1, "", "log_connection"]], "pystream.models.stream": [[1, 1, 1, "", "get_range_header"], [1, 1, 1, "", "range_requests_response"], [1, 1, 1, "", "send_bytes_range_requests"]], "pystream.routers": [[1, 0, 0, "-", "auth"], [1, 0, 0, "-", "basics"], [1, 0, 0, "-", "video"]], "pystream.routers.auth": [[1, 1, 1, "", "login"], [1, 1, 1, "", "logout"]], "pystream.routers.basics": [[1, 1, 1, "", "get_favicon"], [1, 1, 1, "", "root"]], "pystream.routers.video": [[1, 1, 1, "", "stream_video"], [1, 1, 1, "", "video_endpoint"]]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:class", "3": "py:attribute", "4": "py:method"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "class", "Python class"], "3": ["py", "attribute", "Python attribute"], "4": ["py", "method", "Python method"]}, "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, "filter": 1, "ngrok": 1, "squir": 1, "router": 1, "basic": 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, "pystream": [0, 1], "__name__": 0, "__main__": 0, "start": [0, 1], "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], "mandatori": 0, "usernam": [0, 1], "choic": 0, "password": [0, 1], "_": 0, "sourc": [0, 1], "path": [0, 1], "mp4": 0, "option": [0, 1], "host": [0, 1], "ip": [0, 1], "address": [0, 1], "127": [0, 1], "0": [0, 1], "1": [0, 1], "port": [0, 1], "number": 0, "applic": 0, "8000": [0, 1], "websit": [0, 1], "add": [0, 1], "cor": 0, "configur": [0, 1], "worker": [0, 1], "spin": 0, "up": 0, "uvicorn": [0, 1], "server": 0, "docstr": 0, "format": 0, "googl": 0, "style": 0, "convent": 0, "pep": 0, "8": 0, "isort": 0, "requir": [0, 1], "gitvers": 0, "revers": [0, 1], "f": 0, "release_not": 0, "rst": 0, "t": 0, "pre": 0, "commit": 0, "ensur": 0, "run": [0, 1], "pytest": 0, "gener": 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, "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, "kwarg": 1, "starter": 1, "function": 1, "paramet": 1, "keyword": 1, "argument": 1, "env": 1, "class": 1, "rootfilt": 1, "name": 1, "initi": 1, "filter": 1, "log": 1, "while": 1, "preserv": 1, "other": 1, "access": 1, "get": 1, "login": 1, "200": 1, "ok": 1, "307": 1, "temporari": 1, "redirect": 1, "vid_nam": 1, "redund": 1, "each": 1, "iter": 1, "pass": 1, "streamingrespons": 1, "overrid": 1, "implement": 1, "subclass": 1, "The": 1, "method": 1, "record": 1, "examin": 1, "return": 1, "true": 1, "fals": 1, "discard": 1, "which": 1, "togeth": 1, "its": 1, "children": 1, "have": 1, "event": 1, "allow": 1, "through": 1, "If": 1, "i": 1, "specifi": 1, "everi": 1, "logrecord": 1, "bool": 1, "out": 1, "repres": 1, "an": 1, "creat": 1, "time": 1, "someth": 1, "flag": 1, "endpoint": 1, "type": 1, "get_local_ip": 1, "ipv4address": 1, "simpl": 1, "check": 1, "network": 1, "id": 1, "retriev": 1, "local": 1, "privat": 1, "machin": 1, "get_public_ip": 1, "extract": 1, "public": 1, "connect": 1, "make": 1, "request": 1, "extern": 1, "": 1, "async": 1, "verifi": 1, "credenti": 1, "httpbasiccredenti": 1, "jsonrespons": 1, "client": 1, "json": 1, "respons": 1, "content": 1, "statu": 1, "envconfig": 1, "_case_sensit": 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, "video_sourc": 1, "video_port": 1, "int": 1, "url": 1, "ngrok_token": 1, "video_host": 1, "pydant": 1, "share": 1, "across": 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, "field": 1, "variabl": 1, "env_prefix": 1, "extra": 1, "ignor": 1, "hide_input_in_error": 1, "fileio": 1, "index": 1, "html": 1, "list_fil": 1, "static": 1, "chunk_siz": 1, "1048576": 1, "vault": 1, "object": 1, "store": 1, "valu": 1, "info": 1, "dict": 1, "inform": 1, "alia": 1, "get_tunnel": 1, "union": 1, "noreturn": 1, "activ": 1, "tunnel": 1, "httpurl": 1, "run_tunnel": 1, "proxi": 1, "expos": 1, "take": 1, "custom": 1, "log_connect": 1, "onli": 1, "devic": 1, "thi": 1, "avoid": 1, "multipl": 1, "when": 1, "same": 1, "differ": 1, "get_dir_cont": 1, "parent": 1, "subdir": 1, "list": 1, "insid": 1, "particular": 1, "directori": 1, "displai": 1, "subdirectori": 1, "within": 1, "exist": 1, "dictionari": 1, "filepath": 1, "kei": 1, "pair": 1, "get_stream_cont": 1, "folder": 1, "contain": 1, "section": 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, "get_range_head": 1, "range_head": 1, "file_s": 1, "tupl": 1, "proce": 1, "header": 1, "size": 1, "range_requests_respons": 1, "file_path": 1, "given": 1, "auth": 1, "depend": 1, "httpbasic": 1, "templaterespons": 1, "handler": 1, "templat": 1, "logout": 1, "401": 1, "user": 1, "httpexcept": 1, "messag": 1, "get_favicon": 1, "filerespons": 1, "favicon": 1, "ico": 1, "support": 1, "robinhood": 1, "script": 1, "root": 1, "redirectrespons": 1, "render": 1, "stream_video": 1, "video_path": 1, "ha": 1, "video_endpoint": 1, "receiv": 1, "cooki": 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, "-", "ngrok"], [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, "", "ngrok_token"], [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, "", "VAULT"]], "pystream.models.ngrok": [[1, 3, 1, "", "get_tunnel"], [1, 3, 1, "", "run_tunnel"]], "pystream.models.squire": [[1, 3, 1, "", "get_dir_content"], [1, 3, 1, "", "get_stream_content"], [1, 3, 1, "", "log_connection"]], "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, "", "login"], [1, 3, 1, "", "logout"]], "pystream.routers.basics": [[1, 3, 1, "", "get_favicon"], [1, 3, 1, "", "root"]], "pystream.routers.video": [[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, "logger": 1, "util": 1, "model": 1, "authent": 1, "config": 1, "ngrok": 1, "squir": 1, "router": 1, "basic": 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/logger.py b/pystream/logger.py index 6384de8..ce9ea33 100644 --- a/pystream/logger.py +++ b/pystream/logger.py @@ -2,14 +2,37 @@ from uvicorn.logging import ColourizedFormatter -from pystream.models import filters -logging.getLogger("uvicorn.access").addFilter(filters.VideoFilter()) -logging.getLogger("uvicorn.access").addFilter(filters.RootFilter()) +class RootFilter(logging.Filter): + """Class to initiate ``/`` filter in logs while preserving other access logs. + + >>> RootFilter + + See Also: + - Filters ``"GET /login HTTP/1.1" 200 OK``, ``"GET / HTTP/1.1" 307 Temporary Redirect``, ``/video?vid_name=...`` + - Includes redundant logging for each iterable passed in ``StreamingResponse`` + - Overrides logging by implementing a subclass of ``logging.Filter`` + - The method ``filter(record)``, that examines the log record and returns True to log it or False to discard it. + """ + + def filter(self, record: logging.LogRecord) -> bool: + """Filter out logging at ``/`` from log streams. + + Args: + record: ``LogRecord`` represents an event which is created every time something is logged. + + Returns: + bool: + False flag for the endpoint that needs to be filtered. + """ + return record.getMessage().find("/") == -1 + + +logging.getLogger("uvicorn.access").addFilter(RootFilter()) logger = logging.getLogger(name="uvicorn.default") -console_formatter = ColourizedFormatter(fmt="{levelprefix} [{module}:{lineno}] - {message}", style="{", - use_colors=True) +# Use ColourizedFormatter for the logs to blend in with uvicorn's loging +color_formatter = ColourizedFormatter(fmt="{levelprefix} [{module}:{lineno}] - {message}", style="{", use_colors=True) handler = logging.StreamHandler() -handler.setFormatter(fmt=console_formatter) +handler.setFormatter(fmt=color_formatter) logger.addHandler(hdlr=handler) diff --git a/pystream/models/authenticator.py b/pystream/models/authenticator.py index c41e3ab..6009198 100644 --- a/pystream/models/authenticator.py +++ b/pystream/models/authenticator.py @@ -26,7 +26,7 @@ async def verify(credentials: HTTPBasicCredentials) -> JSONResponse: ) username_validation = secrets.compare_digest(credentials.username, config.env.username) - password_validation = secrets.compare_digest(credentials.password, config.env.password) + password_validation = secrets.compare_digest(credentials.password, config.env.password.get_secret_value()) if username_validation and password_validation: return JSONResponse( diff --git a/pystream/models/config.py b/pystream/models/config.py index 04686ca..5adef15 100644 --- a/pystream/models/config.py +++ b/pystream/models/config.py @@ -3,7 +3,8 @@ from ipaddress import IPv4Address from typing import Union -from pydantic import BaseModel, DirectoryPath, Field, HttpUrl, PositiveInt +from pydantic import (BaseModel, DirectoryPath, Field, HttpUrl, PositiveInt, + SecretStr) from pydantic_settings import BaseSettings @@ -15,7 +16,7 @@ class EnvConfig(BaseSettings): """ username: str - password: str + password: SecretStr video_source: DirectoryPath video_port: PositiveInt = 8000 @@ -30,6 +31,7 @@ class Config: env_prefix = "" env_file = os.environ.get("env_file") or os.environ.get("ENV_FILE") or ".env" extra = "ignore" + hide_input_in_errors = True class FileIO(BaseModel): diff --git a/pystream/models/filters.py b/pystream/models/filters.py deleted file mode 100644 index 77bc755..0000000 --- a/pystream/models/filters.py +++ /dev/null @@ -1,47 +0,0 @@ -import logging - - -class VideoFilter(logging.Filter): - """Class to initiate ``/video`` filter in logs while preserving other access logs. - - >>> VideoFilter - - See Also: - - Overrides logging by implementing a subclass of ``logging.Filter`` - - The method ``filter(record)``, that examines the log record and returns True to log it or False to discard it. - """ - - def filter(self, record: logging.LogRecord) -> bool: - """Filter out logging at ``/video`` from log streams. - - Args: - record: ``LogRecord`` represents an event which is created every time something is logged. - - Returns: - bool: - False flag for the endpoint that needs to be filtered. - """ - return record.getMessage().find("/video") == -1 - - -class RootFilter(logging.Filter): - """Class to initiate ``/`` filter in logs while preserving other access logs. - - >>> RootFilter - - See Also: - - Overrides logging by implementing a subclass of ``logging.Filter`` - - The method ``filter(record)``, that examines the log record and returns True to log it or False to discard it. - """ - - def filter(self, record: logging.LogRecord) -> bool: - """Filter out logging at ``/`` from log streams. - - Args: - record: ``LogRecord`` represents an event which is created every time something is logged. - - Returns: - bool: - False flag for the endpoint that needs to be filtered. - """ - return record.getMessage().find("/") == -1 diff --git a/release_notes.rst b/release_notes.rst index 118ac9e..eeb21e9 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -1,6 +1,10 @@ Release Notes ============= +v0.0.1 (12/04/2023) +------------------- +- Let the streaming begin + v0.0.1b (12/04/2023) -------------------- - Install ``ngrok`` as an exteion package only if needed