Skip to content

Commit

Permalink
Reverse the scan_interval logic
Browse files Browse the repository at this point in the history
Scan in background only on demand
Log only filenames for the images section
Add seek buttons underneath the video box
  • Loading branch information
dormant-user committed Dec 11, 2023
1 parent 50dfd77 commit 4c03e80
Show file tree
Hide file tree
Showing 14 changed files with 52 additions and 36 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ 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` for `.mp4` files. Defaults to `30s`, set to `0` to skip
- **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] <br>
Expand Down
7 changes: 6 additions & 1 deletion docs/README.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,13 @@ <h3>Env Variables<a class="headerlink" href="#env-variables" title="Permalink to
<li><p><strong>WORKERS</strong>: Number of workers to spin up the <code class="docutils literal notranslate"><span class="pre">uvicorn</span></code> server. Defaults to <code class="docutils literal notranslate"><span class="pre">1</span></code></p></li>
<li><p><strong>WEBSITE</strong>: Website to add to CORS configuration. <em>Required only if tunneled via CDN</em></p></li>
<li><p><strong>AUTO_THUMBNAIL</strong>: Boolean flag to auto generate thumbnail images for preview. Defaults to <code class="docutils literal notranslate"><span class="pre">True</span></code></p></li>
<li><p><strong>SCAN_INTERVAL</strong>: Interval in seconds to scan <code class="docutils literal notranslate"><span class="pre">VIDEO_SOURCE</span></code> for <code class="docutils literal notranslate"><span class="pre">.mp4</span></code> files. Defaults to <code class="docutils literal notranslate"><span class="pre">30s</span></code>, set to <code class="docutils literal notranslate"><span class="pre">0</span></code> to skip</p></li>
<li><p><strong>SCAN_INTERVAL</strong>: Interval in seconds to scan <code class="docutils literal notranslate"><span class="pre">VIDEO_SOURCE</span></code> in the background. Defaults traditional scan at run time.</p></li>
</ul>
<blockquote>
<div><p>:bulb:   When <code class="docutils literal notranslate"><span class="pre">SCAN_INTERVAL</span></code> is set to <code class="docutils literal notranslate"><span class="pre">None</span></code>, <code class="docutils literal notranslate"><span class="pre">VIDEO_SOURCE</span></code> will be scanned in real time whenever the user
loads the landing page. Setting a <code class="docutils literal notranslate"><span class="pre">SCAN_INTERVAL</span></code> will scan the <code class="docutils literal notranslate"><span class="pre">VIDEO_SOURCE</span></code> in background and reduce runtime
delays in case of complex directory structure.</p>
</div></blockquote>
</section>
</section>
<section id="coding-standards">
Expand Down
5 changes: 4 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ 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` for `.mp4` files. Defaults to `30s`, set to `0` to skip
- **SCAN_INTERVAL**: Interval in seconds to scan `VIDEO_SOURCE` in the background. Defaults traditional scan at run time.
> :bulb: &nbsp; 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] <br>
Expand Down
5 changes: 4 additions & 1 deletion docs/_sources/README.md.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ 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` for `.mp4` files. Defaults to `30s`, set to `0` to skip
- **SCAN_INTERVAL**: Interval in seconds to scan `VIDEO_SOURCE` in the background. Defaults traditional scan at run time.
> :bulb: &nbsp; 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] <br>
Expand Down
11 changes: 2 additions & 9 deletions docs/genindex.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ <h1 id="index">Index</h1>
| <a href="#Q"><strong>Q</strong></a>
| <a href="#R"><strong>R</strong></a>
| <a href="#S"><strong>S</strong></a>
| <a href="#T"><strong>T</strong></a>
| <a href="#U"><strong>U</strong></a>
| <a href="#V"><strong>V</strong></a>
| <a href="#W"><strong>W</strong></a>
Expand Down Expand Up @@ -380,6 +379,8 @@ <h2 id="S">S</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="index.html#pystream.models.config.EnvConfig.scan_interval">scan_interval (pystream.models.config.EnvConfig attribute)</a>
</li>
<li><a href="index.html#pystream.models.squire.scanner">scanner() (in module pystream.models.squire)</a>
</li>
<li><a href="index.html#pystream.models.stream.send_bytes_range_requests">send_bytes_range_requests() (in module pystream.models.stream)</a>
</li>
Expand All @@ -406,14 +407,6 @@ <h2 id="S">S</h2>
</ul></td>
</tr></table>

<h2 id="T">T</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="index.html#pystream.main.task">task() (in module pystream.main)</a>
</li>
</ul></td>
</tr></table>

<h2 id="U">U</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
Expand Down
14 changes: 7 additions & 7 deletions docs/index.html

Large diffs are not rendered by default.

Binary file modified docs/objects.inv
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/searchindex.js

Large diffs are not rendered by default.

13 changes: 4 additions & 9 deletions pystream/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@
app.include_router(video.router)


def task() -> None:
"""Update the shared static object member to response from stream all content."""
config.static.landing_page = squire.get_all_stream_content()


def startup_tasks() -> None:
"""Tasks that need to run during the API startup."""
origins = ["http://localhost.com", "https://localhost.com"]
Expand All @@ -34,7 +29,7 @@ def startup_tasks() -> None:
if config.env.ngrok_token:
kwargs['allow_origin_regex'] = 'https://.*\.ngrok\.io/*' # noqa: W605
app.add_middleware(CORSMiddleware, **kwargs)
task()
squire.scanner()


async def start(**kwargs) -> None:
Expand Down Expand Up @@ -79,11 +74,11 @@ async def start(**kwargs) -> None:
startup_tasks()
if config.env.scan_interval:
# Initiate background task with repeated timer
background_task = scheduler.RepeatedTimer(function=task, interval=config.env.scan_interval)
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.warning("Without background scans, new video files at %s will not reflect in the UI",
config.env.video_source)
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:
Expand Down
2 changes: 1 addition & 1 deletion pystream/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class EnvConfig(BaseSettings):
file_formats: Sequence[str] = (".mov", ".mp4")
video_host: IPv4Address = socket.gethostbyname("localhost")
workers: int = Field(1, le=os.cpu_count(), ge=1, env="WORKERS")
scan_interval: Union[PositiveInt, None] = Field(30, ge=30, le=86_400) # range: 30s to 24h
scan_interval: Union[PositiveInt, None] = Field(None, ge=30, le=86_400) # range: 30s to 24h

class Config:
"""Environment variables configuration."""
Expand Down
10 changes: 5 additions & 5 deletions pystream/models/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def generate_thumbnails(self, interval: int = 1, output_dir: pathlib.PosixPath =
else:
path = os.getcwd()
success, image = self.video_capture.read()
logger.info("Generating thumbnail for %s", self.filepath)
logger.info("Generating thumbnail for '%s'", self.filepath.name)
count = 0
n = 0
while success:
Expand All @@ -57,7 +57,7 @@ def generate_thumbnails(self, interval: int = 1, output_dir: pathlib.PosixPath =
if n:
logger.info("Generated %d thumbnails", n)
return True
logger.error("Failed to generate thumbnails for %s", self.filepath)
logger.error("Failed to generate thumbnails for '%s'", self.filepath.name)

def get_video_length(self) -> Tuple[int, datetime.timedelta]:
"""Get the number of frames to calculate length of the video.
Expand All @@ -84,7 +84,6 @@ def generate_preview(self, path: str, at_second: int = None) -> bool:
Returns a boolean flag to indicate success/failure.
"""
seconds, video_time = self.get_video_length()
logger.info("video duration: %s", video_time)
if os.path.isdir(path):
raise IsADirectoryError("Requires a filepath, received a directory path.")
if os.path.isfile(path):
Expand All @@ -103,6 +102,7 @@ def generate_preview(self, path: str, at_second: int = None) -> bool:
success, image = self.video_capture.read()
if success:
cv2.imwrite(path, image) # save frame as JPEG file
logger.info("Generated preview image for %s at %d seconds", self.filepath, at_second)
logger.info("Generated preview image for '%s' [%s] at %d seconds",
self.filepath.name, video_time, at_second)
return True
logger.error("Failed to generate preview image for %s", self.filepath)
logger.error("Failed to generate preview image for '%s' [%s]", self.filepath.name, video_time)
5 changes: 5 additions & 0 deletions pystream/models/squire.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,8 @@ def get_all_stream_content() -> Dict[str, List[Dict[str, str]]]:
structure['files'] = sorted(structure['files'], key=lambda x: extract_number(x['name']))
structure['directories'] = sorted(structure['directories'], key=lambda x: extract_number(x['name']))
return structure


def scanner() -> None:
"""Update the shared static object member to response from stream all content."""
config.static.landing_page = get_all_stream_content()
1 change: 1 addition & 0 deletions pystream/routers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ async def index(request: Request,
"""
await authenticator.verify(credentials)
squire.log_connection(request)
config.env.scan_interval or squire.scanner()
return templates.TemplateResponse(
name=config.fileio.list_files,
context={"request": request, "logout": config.static.logout_endpoint,
Expand Down
8 changes: 8 additions & 0 deletions pystream/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ <h1>{{title}}</h1>
<a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
</p>
</video>
<button data-skip="-15" class="player__button">« 15s</button>
<button data-skip="15" class="player__button">15s »</button>
</div>
<script>
let origin = window.location.origin; // Get the current origin using JavaScript
Expand All @@ -132,6 +134,12 @@ <h1>{{title}}</h1>
let videoPlayer = document.getElementById("video-player");
// Set the preview source URL for the video-player element
videoPlayer.setAttribute("poster", previewSource);
let skipButtons = document.querySelectorAll("[data-skip]");
skipButtons.forEach((button) => button.addEventListener("click", skip));
function skip() {
// used parseFloat to convert string to number
videoPlayer.currentTime += parseFloat(this.dataset.skip);
}
videoPlayer.load(); // Load the video
// videoPlayer.play(); // Play the video
</script>
Expand Down

0 comments on commit 4c03e80

Please sign in to comment.