ppbbww

Pillar Point boats, birds, and waves watcher
git clone git@abtrout.com:ppbbww.git
Log | Files | Refs | README | LICENSE

commit 919b9004acb8b5fee70c7c72a2c1cb13a6407afb
parent 9471e6dc61aea0a1f56304d513eaccb621753a2f
Author: david cochran <about.trout@gmail.com>
Date:   Sun,  8 Sep 2024 11:17:40 -0700

download stream chunk with aiohttp rather than ffmpeg

Diffstat:
Mppboatwatch/stream_sampler.py | 37++++++++++++++++++++++++++++---------
1 file changed, 28 insertions(+), 9 deletions(-)

diff --git a/ppboatwatch/stream_sampler.py b/ppboatwatch/stream_sampler.py @@ -5,20 +5,28 @@ import logging import os import random import subprocess -import tempfile import time +from tempfile import mkstemp + DEFAULT_HEADERS = { - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0", + "Accept": "*/*", + "Accept-Language": "en-US,en;q=0.5", + "Accept-Encoding": "gzip, deflate, br, zstd", "Origin": "https://www.surfline.com", - "Referer": "https://www.surfline.com/", "DNT": "1", + "Referer": "https://www.surfline.com/", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "cross-site", + "TE": "trailers", } class StreamSampler: def __init__(self, cam_name, data_dir="./data"): - self.base_url = f"https://cams.cdn-surfline.com/cdn-wc/wc-{cam_name}" + self.base_url = f"https://hls.cdn-surfline.com/oregon/wc-{cam_name}" self.cam_name = cam_name self.data_dir = data_dir @@ -29,25 +37,36 @@ class StreamSampler: headers=DEFAULT_HEADERS, ) as session: chunk_url = await self.__get_recent_chunk_url(session) - return self.__extract_frames(chunk_url) + chunk_file = await self.__get_recent_chunk(session, chunk_url) + return self.__extract_frames(chunk_file) async def __get_recent_chunk_url(self, session): - async with session.get(f"{self.base_url}/chunklist.m3u8") as res: + url = f"{self.base_url}/playlist.m3u8" + async with session.get(url) as res: chunklist = await res.text() chunk = next(l for l in chunklist.splitlines() if not l.startswith("#")) return f"{self.base_url}/{chunk}" - def __extract_frames(self, chunk_url): + async def __get_recent_chunk(self, session, chunk_url): + async with session.get(chunk_url) as res: + bytes = await res.read() + fd, chunk_file = mkstemp() + with os.fdopen(fd, "wb") as f: + f.write(bytes) + return chunk_file + + def __extract_frames(self, chunk_file): dt = datetime.datetime.now() day, hour, ts = dt.strftime("%m%d%Y"), dt.strftime("%H"), dt.strftime("%s") frames_dir = f"{self.data_dir}/{self.cam_name}/{day}/{hour}" if not os.path.exists(frames_dir): os.makedirs(frames_dir) subprocess.run(["ffmpeg", - "-hide_banner", "-loglevel", "0", - "-i", chunk_url, + "-hide_banner", "-loglevel", "3", + "-i", chunk_file, "-vf", "select=eq(pict_type\,I)", "-vsync", "vfr", f"{frames_dir}/{ts}-thumb-%04d.jpg"]) + os.remove(chunk_file) # cleanup # Return filenames of (ffmpeg generated) frames to caller. return [f for f in os.scandir(frames_dir) if f.name.startswith(f"{ts}-thumb")]