Stream-Localhost - A secured interface to stream videos¶
Main Module¶
- async pystream.main.redirect_exception_handler(request: Request, exception: RedirectException) JSONResponse ¶
Custom exception handler to handle redirect.
- Parameters:
request – Takes the
Request
object as an argument.exception – Takes the
RedirectException
object inherited fromException
as an argument.
- Returns:
Returns the JSONResponse with content, status code and cookie.
- Return type:
JSONResponse
- async pystream.main.startup_tasks() None ¶
Tasks that need to run during the API startup.
- async pystream.main.shutdown_tasks() None ¶
Tasks that need to run during the API shutdown.
- async pystream.main.start(**kwargs) None ¶
Starter function for the streaming API.
- Parameters:
**kwargs – Keyword arguments to load the env config.
Models¶
Authenticator¶
- async pystream.models.authenticator.failed_auth_counter(request: Request) None ¶
Keeps track of failed login attempts from each host, and redirects if failed for 3 or more times.
- Parameters:
request – Takes the
Request
object as an argument.
- async pystream.models.authenticator.extract_credentials(request: Request) List[str] ¶
Extract the credentials from
Authorization
headers and decode it before returning as a list of strings.
- async pystream.models.authenticator.raise_error(request) NoReturn ¶
Raises a 401 Unauthorized error in case of bad credentials.
- async pystream.models.authenticator.verify_login(request: Request) Dict[str, Union[str, int]] ¶
Verifies authentication and generates session token for each user.
- Returns:
Returns a dictionary with the payload required to create the session token.
- Return type:
Dict[str, str]
- async pystream.models.authenticator.verify_token(token: str) None ¶
Decrypts the symmetric encrypted token and validates the session token and expiration.
- Parameters:
token – Symmetric encrypted key.
Config¶
- pystream.models.config.as_dict(pairs: List[Tuple[str, str]]) Dict[str, str] ¶
Custom decoder for
json.loads
passed viaobject_pairs_hook
raising error on duplicate keys.- Parameters:
pairs – Takes the ordered list of pairs as an argument.
- Returns:
A dictionary of key-value pairs.
- Return type:
Dict[str, str]
- 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, *, authorization: Any, video_source: Path, video_host: IPv4Address = '127.0.0.1', video_port: int = 8000, session_duration: int = 3600, file_formats: Sequence[str] = ('.mov', '.mp4'), workers: int = 1, websites: Optional[List[str]] = [], auto_thumbnail: bool = True, key_file: Optional[Path] = None, cert_file: Optional[Path] = None, secure_session: bool = False)¶
Configure all env vars and validate using
pydantic
to share across modules.>>> EnvConfig
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
__init__ uses __pydantic_self__ instead of the more common self for the first arg to allow self as a field name.
- authorization: Any¶
- video_source: Path¶
- video_host: IPv4Address¶
- video_port: int¶
- session_duration: int¶
- file_formats: Sequence[str]¶
- workers: int¶
- websites: Optional[List[str]]¶
- auto_thumbnail: bool¶
- key_file: Optional[Path]¶
- cert_file: Optional[Path]¶
- secure_session: bool¶
- class Config¶
Environment variables configuration.
- env_prefix = ''¶
- env_file = '.env'¶
- extra = 'ignore'¶
- hide_input_in_errors = True¶
- classmethod parse_authorization(value: Any) Dict[str, SecretStr] ¶
Validates the authorization parameter and converts plain text passwords into
SecretStr
objects.
- classmethod parse_video_host(value: IPv4Address) str ¶
Returns the string notion of IPv4Address object.
- classmethod parse_websites(value: str) List[str] ¶
Evaluates the string as a list and returns the list of strings.
- class pystream.models.config.FileIO(*, index: str = 'index.html', listing: str = 'list.html', landing: str = 'land.html')¶
Loads all the files’ path required/created.
>>> FileIO
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
__init__ uses __pydantic_self__ instead of the more common self for the first arg to allow self as a field name.
- index: str¶
- listing: str¶
- landing: str¶
- class pystream.models.config.Static(*, track: str = 'track', stream: str = 'stream', preview: str = 'preview', query_param: str = 'file', home_endpoint: str = '/home', login_endpoint: str = '/login', logout_endpoint: str = '/logout', streaming_endpoint: str = '/video', chunk_size: int = 1048576, deletions: ~typing.Set[~pathlib.PosixPath] = {}, cipher_suite: ~cryptography.fernet.Fernet = <cryptography.fernet.Fernet object>)¶
Object to store static values.
>>> Static
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
__init__ uses __pydantic_self__ instead of the more common self for the first arg to allow self as a field name.
- track: str¶
- stream: str¶
- preview: str¶
- query_param: str¶
- home_endpoint: str¶
- login_endpoint: str¶
- logout_endpoint: str¶
- streaming_endpoint: str¶
- chunk_size: int¶
- deletions: Set[PosixPath]¶
- cipher_suite: Fernet¶
- class pystream.models.config.Session(*, info: dict = {}, invalid: dict = {}, mapping: dict = {})¶
Object to store session information.
>>> Session
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
__init__ uses __pydantic_self__ instead of the more common self for the first arg to allow self as a field name.
- info: dict¶
- invalid: dict¶
- mapping: dict¶
- class pystream.models.config.WebToken(*, token: str, username: str, timestamp: int)¶
Object to store and validate the symmetric ecrypted payload.
>>> WebToken
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
__init__ uses __pydantic_self__ instead of the more common self for the first arg to allow self as a field name.
- token: str¶
- username: str¶
- timestamp: int¶
- exception pystream.models.config.RedirectException(location: str, detail: Optional[str] = '')¶
Custom
RedirectException
raised within the API since HTTPException doesn’t support returning HTML content.>>> RedirectException
See also
RedirectException allows the API to redirect on demand in cases where returning is not a solution.
There are alternatives to raise HTML content as an exception but none work with our use-case with JavaScript.
This way of exception handling comes handy for many unexpected scenarios.
References
https://fastapi.tiangolo.com/tutorial/handling-errors/#install-custom-exception-handlers
Instantiates the
RedirectException
object with the required parameters.- Parameters:
location – Location for redirect.
detail – Reason for redirect.
Images¶
- class pystream.models.images.Images(filepath: PosixPath)¶
Initiates Images object to generate thumbnails and capture frames for a particular video.
>>> Images
Instantiates the object using opencv’s VideoCapture object.
- Parameters:
filepath – Path of the video file.
- generate_thumbnails(interval: int = 1, output_dir: Optional[PosixPath] = None) bool ¶
Generate thumbnails for a video file.
- Parameters:
interval – Interval in seconds to capture frame as thumbnail.
output_dir – Output directory to store the thumbnails.
- Returns:
Returns a boolean flag to indicate success/failure.
- Return type:
bool
- get_video_length() Tuple[int, timedelta] ¶
Get the number of frames to calculate length of the video.
- Returns:
A tuple of seconds and the timedelta value.
- Return type:
Tuple[int, datetime.timedelta]
- generate_preview(path: str, at_second: Optional[int] = None) bool ¶
Generate preview image for a video.
- Parameters:
path – Filepath to store the preview image.
at_second – Time in seconds at which the image should be captured for preview.
- Returns:
Returns a boolean flag to indicate success/failure.
- Return type:
bool
Squire¶
- pystream.models.squire.log_connection(request: Request) None ¶
Logs the connection information.
See also
Only logs the first connection from a device.
This avoids multiple logs when same device requests different videos.
- pystream.models.squire.natural_sort_key(filename: str) List[Union[int, str]] ¶
Key function for sorting filenames in a natural way.
- Parameters:
filename – Takes the filename as an argument.
- Returns:
Returns a list of elements derived from splitting the filename into parts using a regular expression.
- Return type:
List[Union[int, str]]
- pystream.models.squire.get_dir_stream_content(parent: PosixPath, subdir: str) List[Dict[str, str]] ¶
Get the video files inside a particular directory.
- Parameters:
parent – Parent directory as displayed in the login page.
subdir – Subdirectory within which video files exist.
- Returns:
A list of dictionaries with filename and the filepath as key-value pairs.
- Return type:
List[Dict[str, str]]
- pystream.models.squire.get_all_stream_content() Dict[str, List[Dict[str, str]]] ¶
Get video files or folders that contain video files to be streamed.
- Returns:
Dictionary of files and directories with name and path as key-value pairs on each section.
- Return type:
Dict[str, List[str]]
- pystream.models.squire.get_iter(filename: PurePath) Union[Tuple[str, str], Tuple[None, None]] ¶
Sort video files at the currently served file’s directory, and return the previous and next filenames.
- Parameters:
filename – Path to the video file currently rendered.
- Returns:
Tuple of previous file and next file.
- Return type:
Tuple[str, str]
- pystream.models.squire.remove_thumbnail(img_path: PosixPath) None ¶
Triggered by timer to remove the thumbnail file.
- Parameters:
img_path – PosixPath to the thumbnail image.
- pystream.models.squire.keygen() str ¶
Generate session token from secrets module, so that users are forced to log in when the server restarts.
- Returns:
Returns a URL safe 64-bit token.
- Return type:
str
Stream¶
- pystream.models.stream.send_bytes_range_requests(file_obj: BinaryIO, start_range: int, end_range: int) AsyncIterable[Union[str, ByteString]] ¶
Send a file in chunks using Range Requests specification RFC7233.
- Parameters:
file_obj – File data as bytes.
start_range – Start of range.
end_range – End of range.
- Yields:
ByteString – Bytes as iterable.
- pystream.models.stream.get_range_header(range_header: str, file_size: int) Tuple[int, int] ¶
Proces range header.
- Parameters:
range_header – Range values from the headers.
file_size – Size of the file.
- Returns:
Start and end of the video file.
- Return type:
Tuple
- pystream.models.stream.range_requests_response(range_header: str, file_path: str) StreamingResponse ¶
Returns StreamingResponse using Range Requests of a given file.
- Parameters:
range_header – Range values from the headers.
file_path – Path of the file.
- Returns:
Streaming response from fastapi.
- Return type:
StreamingResponse
Subtitles¶
- async pystream.models.subtitles.srt_to_vtt(filename: PosixPath) None ¶
Convert a .srt file to .vtt for subtitles to be compatible with video-js.
- Parameters:
filename – Name of the srt file.
- async pystream.models.subtitles.vtt_to_srt(filename: PosixPath)¶
Convert a .vtt file to .srt for subtitles to be compatible with video-js.
- Parameters:
filename – Name of the srt file.
Routers¶
Authentication¶
- pystream.routers.auth.get_expiry(lease_start: int, lease_duration: int) str ¶
Get expiry datetime as string using max age.
- Parameters:
lease_start – Time when the authentication was made.
lease_duration – Number of seconds until expiry.
- Returns:
Returns the date and time of expiry in GMT.
- Return type:
str
- async pystream.routers.auth.home_page(request: Request, session_token: str = Cookie(None)) TemplateResponse ¶
Serves the home/index page for the UI.
- Parameters:
request – Takes the
Request
object as an argument.session_token – Session token set after verifying username and password.
- Returns:
Returns the listing page for video streaming.
- Return type:
TemplateResponse
- async pystream.routers.auth.login(request: Request) JSONResponse ¶
Authenticates the user input and returns a redirect response with the session token set as a cookie.
- Parameters:
request – Takes the
Request
object as an argument.- Returns:
Returns the JSONResponse with content, status code and cookie.
- Return type:
JSONResponse
- async pystream.routers.auth.logout(request: Request, session_token: str = Cookie(None)) HTMLResponse ¶
Terminates the user’s session by deleting the cookie and redirecting back to login page upon refresh.
- Parameters:
request – Takes the
Request
object as an argument.session_token – Session token set after verifying username and password.
- Returns:
HTML page for logout with content rendered based on current login status.
- Return type:
HTMLResponse
Basics¶
- async pystream.routers.basics.get_favicon() FileResponse ¶
Gets the favicon.ico and adds to the API endpoint.
- Returns:
Uses FileResponse to send the favicon.ico to support the robinhood script’s robinhood.html.
- Return type:
FileResponse
- async pystream.routers.basics.root(request: Request) RedirectResponse ¶
Reads the root request to render HTMl page.
- Returns:
Redirects to login page.
- Return type:
RedirectResponse
- async pystream.routers.basics.error(detail: str = Cookie(None)) HTMLResponse ¶
Endpoint to serve broken pages as HTML response.
- Parameters:
detail – Optional session related information.
- Returns:
Rendered HTML response with deleted cookie.
- Return type:
HTMLResponse
Video¶
- async pystream.routers.video.preview_loader(request: Request, img_path: str, session_token: str = Cookie(None)) FileResponse ¶
Returns the file for preview image.
- Parameters:
request – Takes the
Request
object as an argument.img_path – Path of the image file that has to be rendered.
session_token – Token setup for each session.
- Returns:
FileResponse for preview image.
- Return type:
FileResponse
- async pystream.routers.video.track_loader(request: Request, track_path: str, session_token: str = Cookie(None)) FileResponse ¶
Returns the file for subtitles.
- Parameters:
request – Takes the
Request
object as an argument.track_path – Path of the subtitle track that has to be rendered.
session_token – Token setup for each session.
- Returns:
FileResponse for subtitle track.
- Return type:
FileResponse
- async pystream.routers.video.stream_video(request: Request, video_path: str, session_token: str = Cookie(None)) TemplateResponse ¶
Returns the template for streaming page.
- Parameters:
request – Takes the
Request
object as an argument.video_path – Path of the video file that has to be rendered.
session_token – Token setup for each session.
- Returns:
Returns the listing page for video streaming.
- Return type:
templates.TemplateResponse
- async pystream.routers.video.video_endpoint(request: Request, range: Optional[str] = Header(None), session_token: str = Cookie(None)) Union[RedirectResponse, StreamingResponse] ¶
Streams the video file by sending bytes using StreamingResponse.
- Parameters:
request – Takes the
Request
object as an argument.range – Header information.
session_token – Token setup for each session.
- Returns:
Streams the video name received as cookie.
- Return type:
Union[RedirectResponse, StreamingResponse]
Support Modules¶
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:
record –
LogRecord
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