From cdd27043a28bcd00a20d2a91c07153c5249785c5 Mon Sep 17 00:00:00 2001 From: Brian McGonagill Date: Fri, 13 Mar 2026 08:48:02 -0500 Subject: [PATCH 1/9] Update to better hand h265 / x265 encoded files. --- app.py | 67 +++++++++++++++++++++++++++++++++++++++------ static/css/main.css | 29 ++++++++++++++++++++ static/js/app.js | 17 +++++++++--- 3 files changed, 101 insertions(+), 12 deletions(-) diff --git a/app.py b/app.py index 97fca89..8d180e4 100644 --- a/app.py +++ b/app.py @@ -62,10 +62,20 @@ VIDEO_EXTENSIONS = { def get_video_info(filepath: str) -> dict | None: + """ + Use ffprobe to get duration, total bitrate, codec, and dimensions. + + Bitrate resolution strategy (handles HEVC/MKV where stream-level + bit_rate is absent): + 1. Stream-level bit_rate — present for H.264/MP4, often missing for HEVC + 2. Format-level bit_rate — reliable for all containers + 3. Derived from size/duration — final fallback + """ cmd = [ 'ffprobe', '-v', 'error', '-select_streams', 'v:0', - '-show_entries', 'format=duration,bit_rate,size:stream=codec_name,width,height', + '-show_entries', + 'format=duration,bit_rate,size:stream=codec_name,width,height,bit_rate', '-of', 'json', filepath, ] @@ -78,15 +88,26 @@ def get_video_info(filepath: str) -> dict | None: stream = (data.get('streams') or [{}])[0] duration = float(fmt.get('duration', 0)) - bit_rate = int(fmt.get('bit_rate', 0)) size_bytes = int(fmt.get('size', 0)) codec = stream.get('codec_name', 'unknown') width = stream.get('width', 0) height = stream.get('height', 0) - if bit_rate == 0 and duration > 0: + # Prefer stream-level bitrate, fall back to format-level, then derive + stream_br = int(stream.get('bit_rate') or 0) + format_br = int(fmt.get('bit_rate') or 0) + if stream_br > 0: + bit_rate = stream_br + elif format_br > 0: + bit_rate = format_br + elif duration > 0: bit_rate = int((size_bytes * 8) / duration) + else: + bit_rate = 0 + # Target ≈ 1/3 of the total bitrate, reserving 128 kbps for audio. + # For HEVC sources the format bitrate already includes audio, so the + # same formula applies regardless of codec. audio_bps = 128_000 video_bps = bit_rate - audio_bps if bit_rate > audio_bps else bit_rate target_video_bps = max(int(video_bps / 3), 200_000) @@ -345,13 +366,21 @@ def run_compression_job(job_id: str) -> None: src_path = file_info['path'] target_bitrate = file_info.get('target_bit_rate_bps', 1_000_000) + src_codec = file_info.get('codec', 'unknown').lower() p = Path(src_path) out_path = str(p.parent / (p.stem + suffix + p.suffix)) + # Choose encoder to match the source codec. + # hevc / h265 / x265 → libx265 + # everything else → libx264 (safe, universally supported) + is_hevc = src_codec in ('hevc', 'h265', 'x265') + encoder = 'libx265' if is_hevc else 'libx264' + push_event(job, { 'type': 'file_start', 'index': idx, 'total': total, 'filename': p.name, 'output': out_path, - 'message': f'Compressing ({idx + 1}/{total}): {p.name}', + 'encoder': encoder, + 'message': f'Compressing ({idx + 1}/{total}): {p.name} [{encoder}]', }) try: @@ -365,12 +394,34 @@ def run_compression_job(job_id: str) -> None: duration_secs = 0 video_k = max(int(target_bitrate / 1000), 200) + + # Build the encoder-specific part of the ffmpeg command. + # + # libx264 uses -maxrate / -bufsize for VBV (Video Buffering Verifier). + # libx265 passes those same constraints via -x265-params because its + # CLI option names differ from the generic ffmpeg flags. + # Both use AAC audio at 128 kbps. + # -movflags +faststart is only meaningful for MP4 containers; it is + # harmless (silently ignored) for MKV/MOV/etc. + if is_hevc: + vbv_maxrate = int(video_k * 1.5) + vbv_bufsize = video_k * 2 + encoder_opts = [ + '-c:v', 'libx265', + '-b:v', f'{video_k}k', + '-x265-params', f'vbv-maxrate={vbv_maxrate}:vbv-bufsize={vbv_bufsize}', + ] + else: + encoder_opts = [ + '-c:v', 'libx264', + '-b:v', f'{video_k}k', + '-maxrate', f'{int(video_k * 1.5)}k', + '-bufsize', f'{video_k * 2}k', + ] + cmd = [ 'ffmpeg', '-y', '-i', src_path, - '-c:v', 'libx264', - '-b:v', f'{video_k}k', - '-maxrate', f'{int(video_k * 1.5)}k', - '-bufsize', f'{video_k * 2}k', + *encoder_opts, '-c:a', 'aac', '-b:a', '128k', '-movflags', '+faststart', '-progress', 'pipe:1', '-nostats', diff --git a/static/css/main.css b/static/css/main.css index 75fc31b..c23a460 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -767,6 +767,35 @@ body { color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; + padding: 2px 7px; + border-radius: var(--radius-pill); + border: 1px solid var(--border-base); + background: var(--bg-card2); + white-space: nowrap; +} + +/* H.265 / HEVC — amber tint to flag it uses libx265 */ +.codec-tag.hevc { + background: rgba(180, 100, 0, 0.10); + border-color: rgba(180, 100, 0, 0.35); + color: #7a4500; +} +[data-theme="dark"] .codec-tag.hevc { + background: rgba(251, 191, 36, 0.12); + border-color: rgba(251, 191, 36, 0.30); + color: #fbbf24; +} + +/* H.264 / AVC — subtle blue tint */ +.codec-tag.h264 { + background: rgba(26, 86, 219, 0.07); + border-color: rgba(26, 86, 219, 0.25); + color: #1e40af; +} +[data-theme="dark"] .codec-tag.h264 { + background: rgba(147, 197, 253, 0.10); + border-color: rgba(147, 197, 253, 0.25); + color: #93c5fd; } /* Checkbox styling */ diff --git a/static/js/app.js b/static/js/app.js index 7c29f4e..d9602c2 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -272,11 +272,19 @@ function showScanStatus(msg, type) { function renderFileTable(files) { let html = ''; files.forEach((f, idx) => { - const sizeFmt = f.size_gb.toFixed(3); + const sizeFmt = f.size_gb.toFixed(3); const curBitrate = f.bit_rate_mbps ? `${f.bit_rate_mbps} Mbps` : 'Unknown'; const tgtBitrate = f.target_bit_rate_mbps ? `${f.target_bit_rate_mbps} Mbps` : '—'; - const codec = f.codec || 'unknown'; - const pathDir = f.path.replace(f.name, ''); + const codec = (f.codec || 'unknown').toLowerCase(); + const pathDir = f.path.replace(f.name, ''); + + // Normalise codec label and pick a CSS modifier for the badge colour + const isHevc = ['hevc', 'h265', 'x265'].includes(codec); + const isH264 = ['h264', 'avc', 'x264'].includes(codec); + const codecLabel = isHevc ? 'H.265 / HEVC' + : isH264 ? 'H.264 / AVC' + : codec.toUpperCase(); + const codecMod = isHevc ? 'hevc' : isH264 ? 'h264' : ''; html += ` @@ -305,7 +313,7 @@ function renderFileTable(files) { ${escHtml(tgtBitrate)} - ${escHtml(codec)} + ${escHtml(codecLabel)} `; }); @@ -370,6 +378,7 @@ els.compressBtn.addEventListener('click', async () => { path: f.path, size_bytes: f.size_bytes, target_bit_rate_bps: f.target_bit_rate_bps || 1000000, + codec: f.codec || 'unknown', })), suffix, }; From b5d9939193eaf1a1d545de4cea2fed507784e0fb Mon Sep 17 00:00:00 2001 From: Brian McGonagill Date: Fri, 13 Mar 2026 09:46:54 -0500 Subject: [PATCH 2/9] Adding screenshots --- README.md | 2 ++ screens/VideoPress_Screen_1.png | Bin 0 -> 102429 bytes screens/VideoPress_Screen_2.png | Bin 0 -> 126848 bytes screens/VideoPress_Screen_3.png | Bin 0 -> 60245 bytes 4 files changed, 2 insertions(+) create mode 100644 screens/VideoPress_Screen_1.png create mode 100644 screens/VideoPress_Screen_2.png create mode 100644 screens/VideoPress_Screen_3.png diff --git a/README.md b/README.md index 9310836..1c1e745 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ containerised with **Docker**. FFmpeg compresses video files to approximately 1/3 their original size. All file-system access is restricted to a single configurable media root for security. + + --- ## Quick start with Docker (recommended) diff --git a/screens/VideoPress_Screen_1.png b/screens/VideoPress_Screen_1.png new file mode 100644 index 0000000000000000000000000000000000000000..bfcf7f075ba30f2cc37ca1c1c6ea1f36499a896a GIT binary patch literal 102429 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

mU`>}YHP=38F~GWi{%e~Xg-N&{AwK6`T}vW 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%0Dur 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 z%0AB#4y>Evm8g)5qx)UMMvLFO6#jlh;V^MG;D29VKOw55bRxUF>Zw~A_=gzs-(oh7<%wDDzq{FqB>1gwjDLcB;xX*C%Cf!<5GAE}vm8hS{vRGDuXR>2V`bdS6f91GWeD z#__|`A_^5W5~08{bY&Vdv}tI>rq98_0fRu?Ur5g>uO(*oFWL>2c=HDhHvR8LlsrdT zw_7e+MZC0Y86Umcsk)b@r>TD=q<%!+WawLf*|vSvb(UKd9nJ0A=J*$8I~E7wKQtgq z-vg1u016A-V}XL;>xBKMy6WCniY(@V9m=?Rbp>9XG?2ugWmTZ8kB{^mug{qbBi|(a z!oK*$dd5cNO+uJMXrd8^ zpzCDmxT&km;9-%AN08~0jO-7rPEOFe;?8P`$W91)^9;<@_dYc>DTzO6e~N-lll@*g z_i+RFRaM*Ka-4pIal4bbert1o{d+yI9_pV{@Z6QIwM4X zKm4;oq9P&UsaLzW;Oh*b)j%`$jwDcUS9?)|SUt*G^~6ZoY!>+HPp%62kb2BxKWSXf zt!ywGcm{H2^?>ObBMKEoDF&f%SgT~k^YiO+@@3E>MOozwKdUTE#lxvui)Aahb*rZD z#OJq<4&FjJ#vL$=TAWtG=TVtw2&aVSW{4_MNJ2`1IW{a`7(Xj}!Nt`iRP^AXCe^cy z!3VEiy;8KL3*IUG^mJj!dXYk~0EM(rHLDbEH48D)%)46uG=R#a`d~gukD8r@{U8EO z5OZL)5lO%d1e_m`sawpQh`LISU*3CTCv}J0L_=6eh~b4UyiaU%Nmt1p|JM_}4bQJ8 zdpd1|=QXv{i;;u(B=d$pgAfSW)1^Bsy5zX&56ZF&7@QJI?yIZY;Jdu*1uhKEGf98& zAL=hSuG!6aQT+q8`S(ICBN>zF*IUp0r!XE$7{@bKL(TRF#wEuQJh9xt7KH-ZJbBy^v4vb70UN*6r~m)} literal 0 HcmV?d00001 diff --git a/screens/VideoPress_Screen_3.png b/screens/VideoPress_Screen_3.png new file mode 100644 index 0000000000000000000000000000000000000000..43932999921456b8d32b16d1b38a6578a794ff7d GIT binary patch literal 60245 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 DwQ5qc literal 0 HcmV?d00001 From 4c33cb48a98808d9d77bd85edaf8c5075f153995 Mon Sep 17 00:00:00 2001 From: Brian McGonagill Date: Fri, 13 Mar 2026 09:49:05 -0500 Subject: [PATCH 3/9] Adding Screenshot 1 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c1e745..f5a2c1a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ containerised with **Docker**. FFmpeg compresses video files to approximately 1/3 their original size. All file-system access is restricted to a single configurable media root for security. - +![Image 1](screens/VideoPress_Screen_1.png) --- From b48784e2ad0dc3b57fbb75fe9eec53cce0fd6531 Mon Sep 17 00:00:00 2001 From: Brian McGonagill Date: Fri, 13 Mar 2026 09:53:49 -0500 Subject: [PATCH 4/9] Image Update --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index f5a2c1a..d0aacca 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,18 @@ containerised with **Docker**. FFmpeg compresses video files to approximately 1/3 their original size. All file-system access is restricted to a single configurable media root for security. +1. Set your folder to scan, and minimum size to scan for. + ![Image 1](screens/VideoPress_Screen_1.png) +2. Select from the files found. + +![Image 2](screens/VideoPress_Screen_2.png) + +3. Start the Compression and See the Progress + +![Image 3](screens/VideoPress_Screen_3.png) + --- ## Quick start with Docker (recommended) From 7e0502ca403b78efb0fe3659f2904310422e5b10 Mon Sep 17 00:00:00 2001 From: Brian McGonagill Date: Tue, 17 Mar 2026 14:01:35 -0500 Subject: [PATCH 5/9] Updates galore. Improved folder structure, componentized, and notifications upon completion. --- Dockerfile | 10 +- __pycache__/app.cpython-313.pyc | Bin 0 -> 22992 bytes app.py => app.py.archive | 0 app/__init__.py | 37 ++ app/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 1212 bytes app/__pycache__/config.cpython-313.pyc | Bin 0 -> 2231 bytes app/__pycache__/jobs.cpython-313.pyc | Bin 0 -> 13317 bytes app/__pycache__/media.cpython-313.pyc | Bin 0 -> 5241 bytes app/__pycache__/notify.cpython-313.pyc | Bin 0 -> 11178 bytes app/__pycache__/routes.cpython-313.pyc | Bin 0 -> 11170 bytes app/config.py | 51 ++ app/db.py | 142 +++++ app/jobs.py | 349 +++++++++++ app/media.py | 140 +++++ app/notify.py | 329 ++++++++++ app/routes.py | 470 +++++++++++++++ docker-compose.yml | 47 +- run.py | 22 + start.sh | 4 +- static/css/main.css | 276 ++++++++- static/js/app.js | 729 +---------------------- static/js/modules/browser.js | 111 ++++ static/js/modules/compress.js | 195 ++++++ static/js/modules/progress.js | 172 ++++++ static/js/modules/scan.js | 194 ++++++ static/js/modules/session.js | 59 ++ static/js/modules/settings.js | 260 ++++++++ static/js/modules/state.js | 119 ++++ static/js/modules/stream.js | 276 +++++++++ static/js/modules/theme.js | 46 ++ static/js/modules/utils.js | 45 ++ templates/index.html | 193 +++++- wsgi.py | 17 +- 33 files changed, 3565 insertions(+), 728 deletions(-) create mode 100644 __pycache__/app.cpython-313.pyc rename app.py => app.py.archive (100%) create mode 100644 app/__init__.py create mode 100644 app/__pycache__/__init__.cpython-313.pyc create mode 100644 app/__pycache__/config.cpython-313.pyc create mode 100644 app/__pycache__/jobs.cpython-313.pyc create mode 100644 app/__pycache__/media.cpython-313.pyc create mode 100644 app/__pycache__/notify.cpython-313.pyc create mode 100644 app/__pycache__/routes.cpython-313.pyc create mode 100644 app/config.py create mode 100644 app/db.py create mode 100644 app/jobs.py create mode 100644 app/media.py create mode 100644 app/notify.py create mode 100644 app/routes.py create mode 100644 run.py mode change 100644 => 100755 start.sh create mode 100644 static/js/modules/browser.js create mode 100644 static/js/modules/compress.js create mode 100644 static/js/modules/progress.js create mode 100644 static/js/modules/scan.js create mode 100644 static/js/modules/session.js create mode 100644 static/js/modules/settings.js create mode 100644 static/js/modules/state.js create mode 100644 static/js/modules/stream.js create mode 100644 static/js/modules/theme.js create mode 100644 static/js/modules/utils.js diff --git a/Dockerfile b/Dockerfile index abd1a99..26119cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,8 @@ RUN groupadd --gid 1000 appuser \ # ── Application code ───────────────────────────────────────────────────────── WORKDIR /app -COPY app.py wsgi.py gunicorn.conf.py requirements.txt ./ +COPY app/ app/ +COPY wsgi.py run.py gunicorn.conf.py requirements.txt ./ COPY templates/ templates/ COPY static/ static/ @@ -61,6 +62,10 @@ COPY static/ static/ # All file-system access by the application is restricted to this path. RUN mkdir -p /media && chown appuser:appuser /media +# ── Data directory for the SQLite settings database ────────────────────────── +# Mounted as a named volume in docker-compose so settings survive restarts. +RUN mkdir -p /data && chown appuser:appuser /data + # ── File ownership ──────────────────────────────────────────────────────────── RUN chown -R appuser:appuser /app @@ -73,6 +78,7 @@ USER appuser # PORT — TCP port Gunicorn listens on (exposed below). # LOG_LEVEL — Gunicorn log verbosity (debug | info | warning | error). ENV MEDIA_ROOT=/media \ + DB_PATH=/data/videopress.db \ PORT=8080 \ LOG_LEVEL=info \ PYTHONUNBUFFERED=1 \ @@ -89,4 +95,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ || exit 1 # ── Start Gunicorn ──────────────────────────────────────────────────────────── -CMD ["gunicorn", "-c", "gunicorn.conf.py", "wsgi:app"] +CMD ["gunicorn", "-c", "gunicorn.conf.py", "wsgi:application"] diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b50a36075371fa874cfaefe2a5bb1f61789f6be GIT binary patch literal 22992 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 Flask: + """ + Create and return a configured Flask application instance. + """ + flask_app = Flask( + __name__, + template_folder=str(BASE_DIR / 'templates'), + static_folder=str(BASE_DIR / 'static'), + ) + + # Initialise the SQLite settings database + init_db() + + register_routes(flask_app) + + return flask_app diff --git a/app/__pycache__/__init__.cpython-313.pyc b/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..22033f0c810d7a0b9887adb601be4d9a14b6844b GIT binary patch literal 1212 zcmZuw&ubGw6rSB|HpytQ)mrKgWJC`M*aZA>5VfGTYFkurTM#NN>+U2SyO~+uOd{1w z{{$}{1P@-l`-docS}F)0MDUYDFJp^4`q%Ufz4(m+i4JO3+Tccq6(MLO$DI zWbTf$J&VqB5)dGP6Szyxq6;n~K_&2(DvKU?i&dx=adpXGtU(QZ|ABkn4QfZpBM<7m zdaLo|8;!E86NW+x6Nc?kLt;h$ADEWZft>3O3r5uIQ-8>5MhXSQk{^ z0-5M%3Ji;ZM}~)ZV;^l9>$59+9-1vmZw{o06-d^6ta}1mXt6j-Q~ei=DH(GX8KzAH zW{|b%@)~CeAI@kFLk^5f0~;(;LK@bDfh_7Iifd*NsG~k_)30`QEhn!QGktYzwNmd)9{FU8YMpnpa(()o7i_z=F%2^~6 zRyNBb7Ax5my#erq{mZ40xKLazlDW+LS&D?|A|0=dh*?*p_5wK8(<&We5#WdtL(YsU zcAJ5E#ACxiDMOp1Wg^WPrXd@ui!h-vjMq^vtusukold$!CIvx#>=FR0q#G319zlXX ztIJf*9xGTEvd0o(At=3)(~@|soJIo~uVHIefo+;M%xIL{FXup-4~#Cuu1XUQ`B?e1 z6Sk^gdCco!C{Zk7m{+s`u-rin4-`aWSHs#8$MSQXHPz>xhae)^nd3vAs;rMAcZMS2 zD$H&~nb z;;Pi@sfYtqZAGeHa;o6gLl3Bz3aL`Bv3v;HhEq?y8HGw!PyJ^dCoXVcw7c{CAK(A~ z-|Tiew`N)>pUBw4}m9$(Duoq`Kfg=TEkFq8onp)}k*O)PICPsd_#i z%h*P>taC#M! zWx`gm4QCk6*+#)6y{bBFn%F5=L{FOpFY9K3aGcRCoF>?Y^Nbn>vG6k0F|n2@v#lH$ zhl7j?2_9EfjHkv%$A?pinVIVtTEey+Y*0orjt%yNO8~~CnVOq9eHAmJ8?hBe9fG?J zlGO{Q6UVU}F{mB|ebY17$1i`9x}2Dq0)0th8Q`&Q7>saULqjcW*$&Otm69Cba(QzmipGb$80gH zOIp6Y-8pG@RsQ%)d<7x>B0_WW>v%ioo8M(@qP?zvukUHF(Y>>5yp7Pjz~i-~Z%)!s zQr`0kX8yCgw*`^rqzF2K=KXbv0Mfw{Zqy;op$k5bUprADQWjm5-$hv|+Ejd|CJ499 zWr9yacu!)-2CiT|4QmCcK*1M}JUZtDYK6kXF||14s-d)jD+w9$0v%%P6$MH z!SNt@gfvZi1)TRNhnZk(chMK^jkYjRLvCOPp4&2ZD?{?)esM!DA#!EzFecLCs@&0; z!&fMfz-M#VA^E7EiQj~))FTBWu#*BIR}nE6BQC@R+uZkKOLmUL(r~X>u~ES`J1?%9 zm`9nE$C=pwE=9M6>sgW#s+jl%MB(K>z^8<&?Q+w=R`|f0{LSEII8qKrD&gZ_DK8Y% z9{x6d_lwfitx(7PfxDlVu09QfDuF}mS2rX5&u`RB!c6x_d!B(SX=Ue_M- zM~fR>eHE>LL+J;Rrh$KXEBN>2!QnylWUzB2EJr)sKq_U}nN*4iBDu}n&fFx98xXKe zny#foNu{#XBydP@*lo`13_|0!&JJIi7``%=8XZr#%E)lCHVW+A)ULcpoLYhcFv@;v zSgc9N2a{vdqf^7VfFMyBGHVhF9i!;iF+Am^=9fhf&l^0upAxY1Dt&)Gc z6G`eHsO=eg=XpB{g&~~QrWPq{k&1SFQ|l>fJ*C#C+QCf?mo;2co;Qm_XCB1Ajz1Xs zdT9OBPf8^`SkdB}+DKU&sc54mwHie29rucNiuZ2cxxJQq_}SyWUx$7fs(di95ue=X gnW}`QL1*Z_Qm`7_C+)j`wu+!zJNLKf*b#8@KPX8mbpQYW literal 0 HcmV?d00001 diff --git a/app/__pycache__/jobs.cpython-313.pyc b/app/__pycache__/jobs.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..991aa163699e5f8841ce785fccf0a7bf9cfe2bed GIT binary patch literal 13317 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 GQTaarT|T7% literal 0 HcmV?d00001 diff --git a/app/__pycache__/notify.cpython-313.pyc b/app/__pycache__/notify.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b377732c0705351ef50c653d838fe4db4559bd7 GIT binary patch literal 11178 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 literal 0 HcmV?d00001 diff --git a/app/__pycache__/routes.cpython-313.pyc b/app/__pycache__/routes.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fb84cf711abe75ec082358008aaacc8dfa0e4f9 GIT binary patch literal 11170 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 MJJa$9_RCiNKSVBiwEzGB literal 0 HcmV?d00001 diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..d130e30 --- /dev/null +++ b/app/config.py @@ -0,0 +1,51 @@ +""" +app/config.py +============= +Central configuration and the path-jail helper used by every other module. + +All tuneable values can be overridden via environment variables: + + MEDIA_ROOT Root directory the application may read/write (default: /media) + DB_PATH Path to the SQLite database file (default: /videopress.db) + PORT TCP port Gunicorn listens on (default: 8080) + LOG_LEVEL Gunicorn log verbosity (default: info) +""" + +import os +from pathlib import Path + +# --------------------------------------------------------------------------- +# Paths +# --------------------------------------------------------------------------- + +PACKAGE_DIR = Path(__file__).resolve().parent # …/app/ +BASE_DIR = PACKAGE_DIR.parent # …/videocompressor/ + +# Every file-system operation in the application is restricted to MEDIA_ROOT. +MEDIA_ROOT = Path(os.environ.get('MEDIA_ROOT', '/media')).resolve() + +# --------------------------------------------------------------------------- +# Path-jail helper +# --------------------------------------------------------------------------- + +def safe_path(raw: str) -> Path: + """ + Resolve *raw* to an absolute path and assert it is inside MEDIA_ROOT. + + Returns the resolved Path on success. + Raises PermissionError if the path would escape MEDIA_ROOT (including + symlink traversal and ../../ attacks). + """ + try: + resolved = Path(raw).resolve() + except Exception: + raise PermissionError(f"Invalid path: {raw!r}") + + root_str = str(MEDIA_ROOT) + path_str = str(resolved) + if path_str != root_str and not path_str.startswith(root_str + os.sep): + raise PermissionError( + f"Access denied: '{resolved}' is outside the allowed " + f"media root ({MEDIA_ROOT})." + ) + return resolved diff --git a/app/db.py b/app/db.py new file mode 100644 index 0000000..b916a55 --- /dev/null +++ b/app/db.py @@ -0,0 +1,142 @@ +""" +app/db.py +========= +Lightweight SQLite-backed key/value settings store. + +The database file is created automatically on first use beside the +application package, or at the path set by the DB_PATH environment +variable (useful for Docker volume persistence). + +Public API +---------- + init_db() — create the table if it doesn't exist (call at startup) + get_setting(key) — return the stored string value, or None + save_setting(key, val) — upsert a key/value pair + get_all_settings() — return all rows as {key: value} + delete_setting(key) — remove a key (used to clear optional fields) +""" + +import os +import sqlite3 +import threading +from pathlib import Path + +from .config import BASE_DIR + +# --------------------------------------------------------------------------- +# Database location +# --------------------------------------------------------------------------- + +# Default: videocompressor/videopress.db — sits beside the app/ package. +# Override with the DB_PATH env var (e.g. to a Docker-mounted volume path). +DB_PATH = Path(os.environ.get('DB_PATH', str(BASE_DIR / 'videopress.db'))) + +# SQLite connections are not thread-safe across threads; use a per-thread +# connection via threading.local() so each worker greenlet/thread gets its own. +_local = threading.local() + +_INIT_LOCK = threading.Lock() +_initialised = False + + +def _connect() -> sqlite3.Connection: + """Return (and cache) a per-thread SQLite connection.""" + if not hasattr(_local, 'conn') or _local.conn is None: + _local.conn = sqlite3.connect(str(DB_PATH), check_same_thread=False) + _local.conn.row_factory = sqlite3.Row + # WAL mode allows concurrent reads alongside a single writer + _local.conn.execute('PRAGMA journal_mode=WAL') + _local.conn.execute('PRAGMA foreign_keys=ON') + return _local.conn + + +# --------------------------------------------------------------------------- +# Schema +# --------------------------------------------------------------------------- + +def init_db() -> None: + """ + Create the settings table if it does not already exist. + Also creates the parent directory of DB_PATH if needed. + Safe to call multiple times — idempotent. + """ + global _initialised + with _INIT_LOCK: + if _initialised: + return + + # Ensure the directory exists before SQLite tries to create the file. + # This handles the case where the Docker volume mount creates ./data + # as root before the container user can write to it. + db_dir = DB_PATH.parent + try: + db_dir.mkdir(parents=True, exist_ok=True) + except PermissionError: + raise PermissionError( + f"Cannot create database directory '{db_dir}'. " + f"If running in Docker, create the directory on the host first " + f"and ensure it is writable by UID 1000:\n" + f" mkdir -p {db_dir} && chown 1000:1000 {db_dir}" + ) + + # Test that we can actually write to the directory before SQLite tries + test_file = db_dir / '.write_test' + try: + test_file.touch() + test_file.unlink() + except PermissionError: + raise PermissionError( + f"Database directory '{db_dir}' is not writable by the current user. " + f"If running in Docker, fix permissions on the host:\n" + f" chown 1000:1000 {db_dir}" + ) + + conn = _connect() + conn.execute(""" + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ) + """) + conn.commit() + _initialised = True + + +# --------------------------------------------------------------------------- +# CRUD helpers +# --------------------------------------------------------------------------- + +def get_setting(key: str) -> str | None: + """Return the stored value for *key*, or None if not set.""" + init_db() + row = _connect().execute( + 'SELECT value FROM settings WHERE key = ?', (key,) + ).fetchone() + return row['value'] if row else None + + +def save_setting(key: str, value: str) -> None: + """Insert or update *key* with *value*.""" + init_db() + conn = _connect() + conn.execute( + 'INSERT INTO settings (key, value) VALUES (?, ?)' + ' ON CONFLICT(key) DO UPDATE SET value = excluded.value', + (key, value), + ) + conn.commit() + + +def delete_setting(key: str) -> None: + """Remove *key* from the store (silently succeeds if absent).""" + init_db() + conn = _connect() + conn.execute('DELETE FROM settings WHERE key = ?', (key,)) + conn.commit() + + +def get_all_settings() -> dict[str, str]: + """Return all stored settings as a plain dict.""" + init_db() + rows = _connect().execute('SELECT key, value FROM settings').fetchall() + return {row['key']: row['value'] for row in rows} diff --git a/app/jobs.py b/app/jobs.py new file mode 100644 index 0000000..f707bd8 --- /dev/null +++ b/app/jobs.py @@ -0,0 +1,349 @@ +""" +app/jobs.py +=========== +In-process job store and the ffmpeg compression worker thread. + +Design note: job state is kept in a plain dict protected by a threading.Lock. +This is intentional — VideoPress uses a single Gunicorn worker process +(required for SSE streaming with gevent), so cross-process state sharing is +not needed. If you ever move to multiple workers, replace `active_jobs` with +a Redis-backed store and remove the threading.Lock. + +Public API +---------- + active_jobs : dict {job_id -> job_dict} + job_lock : Lock protects mutations to active_jobs + push_event() : append an SSE event to a job's event queue + run_compression_job(): worker — called in a daemon thread +""" + +import os +import subprocess +import threading +import time +from pathlib import Path + +from .notify import send_completion_email + +# --------------------------------------------------------------------------- +# Job store +# --------------------------------------------------------------------------- + +active_jobs: dict = {} +job_lock = threading.Lock() + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def push_event(job: dict, event: dict) -> None: + """Append *event* to job['events'] under the job's own lock.""" + with job['lock']: + job['events'].append(event) + + +def _choose_encoder(codec: str) -> tuple[str, bool]: + """ + Return (ffmpeg_encoder_name, is_hevc) for the given source codec string. + + HEVC / H.265 sources are re-encoded with libx265 to preserve efficiency. + Everything else uses libx264 (universally supported, always available). + """ + normalised = codec.lower() + is_hevc = normalised in ('hevc', 'h265', 'x265') + encoder = 'libx265' if is_hevc else 'libx264' + return encoder, is_hevc + + +def _build_ffmpeg_cmd( + src: str, + out: str, + video_k: int, + is_hevc: bool, + encoder: str, +) -> list[str]: + """ + Build the ffmpeg command list for one file. + + libx264 accepts -maxrate / -bufsize directly. + libx265 requires those same constraints via -x265-params because its + CLI option names differ from the generic ffmpeg flags. + Both use AAC audio at 128 kbps. + -movflags +faststart is only meaningful for MP4 containers but is + silently ignored for MKV / MOV / etc., so it is always included. + """ + if is_hevc: + vbv_maxrate = int(video_k * 1.5) + vbv_bufsize = video_k * 2 + encoder_opts = [ + '-c:v', encoder, + '-b:v', f'{video_k}k', + '-x265-params', f'vbv-maxrate={vbv_maxrate}:vbv-bufsize={vbv_bufsize}', + ] + else: + encoder_opts = [ + '-c:v', encoder, + '-b:v', f'{video_k}k', + '-maxrate', f'{int(video_k * 1.5)}k', + '-bufsize', f'{video_k * 2}k', + ] + + return [ + 'ffmpeg', '-y', '-i', src, + *encoder_opts, + '-c:a', 'aac', '-b:a', '128k', + '-movflags', '+faststart', + '-progress', 'pipe:1', '-nostats', + out, + ] + + +def _get_duration(filepath: str) -> float: + """Return the duration of *filepath* in seconds, or 0.0 on failure.""" + try: + probe = subprocess.run( + ['ffprobe', '-v', 'error', + '-show_entries', 'format=duration', + '-of', 'default=noprint_wrappers=1:nokey=1', + filepath], + capture_output=True, text=True, timeout=30, + ) + return float(probe.stdout.strip()) if probe.stdout.strip() else 0.0 + except Exception: + return 0.0 + + +def _send_notification(job: dict, email_results: list[dict], cancelled: bool) -> None: + """Send email and push a 'notify' event regardless of outcome.""" + notify_email = job.get('notify_email', '') + if not notify_email: + return + ok, err = send_completion_email(notify_email, email_results, cancelled) + push_event(job, { + 'type': 'notify', + 'success': ok, + 'message': (f'Notification sent to {notify_email}.' if ok + else f'Could not send notification: {err}'), + }) + + +# --------------------------------------------------------------------------- +# Compression worker +# --------------------------------------------------------------------------- + +def run_compression_job(job_id: str) -> None: + """ + Worker function executed in a daemon thread for each compression job. + + Iterates over the file list, runs ffmpeg for each file, streams progress + events, and sends an email notification when finished (if requested). + """ + with job_lock: + job = active_jobs.get(job_id) + if not job: + return + + files = job['files'] + suffix = job['suffix'] + total = job['total'] + + push_event(job, { + 'type': 'start', + 'total': total, + 'message': f'Starting compression of {total} file(s)', + }) + + for idx, file_info in enumerate(files): + + # ── Cancellation check ──────────────────────────────────────────── + with job['lock']: + cancelled = job['cancelled'] + if cancelled: + _handle_cancel(job, idx) + return + + # ── Per-file setup ──────────────────────────────────────────────── + src_path = file_info['path'] + target_bitrate = file_info.get('target_bit_rate_bps', 1_000_000) + src_codec = file_info.get('codec', 'unknown') + p = Path(src_path) + out_path = str(p.parent / (p.stem + suffix + p.suffix)) + encoder, is_hevc = _choose_encoder(src_codec) + video_k = max(int(target_bitrate / 1000), 200) + + push_event(job, { + 'type': 'file_start', + 'index': idx, + 'total': total, + 'filename': p.name, + 'output': out_path, + 'encoder': encoder, + 'message': f'Compressing ({idx + 1}/{total}): {p.name} [{encoder}]', + }) + + duration_secs = _get_duration(src_path) + cmd = _build_ffmpeg_cmd(src_path, out_path, video_k, is_hevc, encoder) + + # ── Run ffmpeg ──────────────────────────────────────────────────── + try: + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, + ) + with job['lock']: + job['process'] = proc + + _stream_progress(job, proc, idx, duration_secs) + proc.wait() + + with job['lock']: + cancelled = job['cancelled'] + + if cancelled: + _remove_partial(out_path) + _handle_cancel(job, idx) + return + + if proc.returncode != 0: + _push_file_error(job, idx, p.name, proc) + else: + _push_file_done(job, idx, p.name, out_path, file_info) + + with job['lock']: + job['current_index'] = idx + 1 + + except Exception as exc: + push_event(job, { + 'type': 'file_error', + 'index': idx, + 'filename': p.name, + 'message': f'Exception: {exc}', + }) + + # ── All files processed ─────────────────────────────────────────────── + push_event(job, { + 'type': 'done', + 'message': f'All {total} file(s) processed.', + }) + with job['lock']: + job['status'] = 'done' + all_events = list(job['events']) + + completed = [{'status': 'done', **e} for e in all_events if e.get('type') == 'file_done'] + errored = [{'status': 'error', **e} for e in all_events if e.get('type') == 'file_error'] + _send_notification(job, completed + errored, cancelled=False) + + +# --------------------------------------------------------------------------- +# Private sub-helpers +# --------------------------------------------------------------------------- + +def _stream_progress( + job: dict, + proc: subprocess.Popen, + idx: int, + duration_secs: float, +) -> None: + """Read ffmpeg's -progress output and push progress events.""" + for line in proc.stdout: + with job['lock']: + if job['cancelled']: + proc.terminate() + return + + line = line.strip() + if '=' not in line: + continue + key, _, value = line.partition('=') + key, value = key.strip(), value.strip() + + if key == 'out_time_ms' and duration_secs > 0: + try: + elapsed = int(value) / 1_000_000 + pct = min(100.0, (elapsed / duration_secs) * 100) + push_event(job, { + 'type': 'progress', + 'index': idx, + 'percent': round(pct, 1), + 'elapsed_secs': round(elapsed, 1), + 'duration_secs': round(duration_secs, 1), + }) + except (ValueError, ZeroDivisionError): + pass + elif key == 'progress' and value == 'end': + push_event(job, { + 'type': 'progress', + 'index': idx, + 'percent': 100.0, + 'elapsed_secs': duration_secs, + 'duration_secs': duration_secs, + }) + + +def _remove_partial(path: str) -> None: + try: + if os.path.exists(path): + os.remove(path) + except OSError: + pass + + +def _handle_cancel(job: dict, idx: int) -> None: + """Push cancel event, set status, send notification for cancelled run.""" + push_event(job, {'type': 'cancelled', 'message': 'Compression cancelled by user'}) + with job['lock']: + job['status'] = 'cancelled' + all_events = list(job['events']) + + completed = [{'status': 'done', **e} for e in all_events if e.get('type') == 'file_done'] + errored = [{'status': 'error', **e} for e in all_events if e.get('type') == 'file_error'] + _send_notification(job, completed + errored, cancelled=True) + + +def _push_file_error( + job: dict, + idx: int, + filename: str, + proc: subprocess.Popen, +) -> None: + try: + tail = proc.stderr.read()[-500:] + except Exception: + tail = '' + push_event(job, { + 'type': 'file_error', + 'index': idx, + 'filename': filename, + 'message': f'ffmpeg exited with code {proc.returncode}', + 'detail': tail, + }) + + +def _push_file_done( + job: dict, + idx: int, + filename: str, + out_path: str, + file_info: dict, +) -> None: + try: + out_sz = os.path.getsize(out_path) + out_gb = round(out_sz / (1024 ** 3), 3) + orig_sz = file_info.get('size_bytes', 0) + reduction = round((1 - out_sz / orig_sz) * 100, 1) if orig_sz else 0 + except OSError: + out_gb = 0 + reduction = 0 + + push_event(job, { + 'type': 'file_done', + 'index': idx, + 'filename': filename, + 'output': out_path, + 'output_size_gb': out_gb, + 'reduction_pct': reduction, + 'message': f'Completed: {filename} → saved {reduction}%', + }) diff --git a/app/media.py b/app/media.py new file mode 100644 index 0000000..83d1b3f --- /dev/null +++ b/app/media.py @@ -0,0 +1,140 @@ +""" +app/media.py +============ +File-system scanning and FFprobe metadata helpers. + +Public API +---------- + VIDEO_EXTENSIONS : frozenset of lowercase video file suffixes + get_video_info() : run ffprobe on a single file, return a metadata dict + list_video_files(): walk a directory tree and return files above a size floor +""" + +import json +import os +import subprocess +from pathlib import Path + +# --------------------------------------------------------------------------- +# Constants +# --------------------------------------------------------------------------- + +VIDEO_EXTENSIONS: frozenset[str] = frozenset({ + '.mp4', '.mkv', '.mov', '.avi', '.wmv', '.flv', + '.webm', '.m4v', '.mpg', '.mpeg', '.ts', '.mts', + '.m2ts', '.vob', '.ogv', '.3gp', '.3g2', +}) + +# --------------------------------------------------------------------------- +# FFprobe helper +# --------------------------------------------------------------------------- + +def get_video_info(filepath: str) -> dict | None: + """ + Use ffprobe to get duration, total bitrate, codec, and dimensions. + + Returns a dict with the keys below, or None if ffprobe fails. + + Bitrate resolution order (handles HEVC/MKV where the stream-level + bit_rate field is absent): + 1. Stream-level bit_rate — present for H.264/MP4, often missing for HEVC + 2. Format-level bit_rate — reliable for all containers + 3. Derived from size / duration — final fallback + + Returned keys + ------------- + duration, bit_rate_bps, bit_rate_mbps, + target_bit_rate_bps, target_bit_rate_mbps, + size_bytes, size_gb, codec, width, height + """ + cmd = [ + 'ffprobe', '-v', 'error', + '-select_streams', 'v:0', + '-show_entries', + 'format=duration,bit_rate,size:stream=codec_name,width,height,bit_rate', + '-of', 'json', + filepath, + ] + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) + if result.returncode != 0: + return None + + data = json.loads(result.stdout) + fmt = data.get('format', {}) + stream = (data.get('streams') or [{}])[0] + + duration = float(fmt.get('duration', 0)) + size_bytes = int(fmt.get('size', 0)) + codec = stream.get('codec_name', 'unknown') + width = stream.get('width', 0) + height = stream.get('height', 0) + + stream_br = int(stream.get('bit_rate') or 0) + format_br = int(fmt.get('bit_rate') or 0) + if stream_br > 0: + bit_rate = stream_br + elif format_br > 0: + bit_rate = format_br + elif duration > 0: + bit_rate = int((size_bytes * 8) / duration) + else: + bit_rate = 0 + + # Target ≈ 1/3 of the total bitrate; reserve 128 kbps for audio. + audio_bps = 128_000 + video_bps = bit_rate - audio_bps if bit_rate > audio_bps else bit_rate + target_video_bps = max(int(video_bps / 3), 200_000) + + return { + 'duration': duration, + 'bit_rate_bps': bit_rate, + 'bit_rate_mbps': round(bit_rate / 1_000_000, 2), + 'target_bit_rate_bps': target_video_bps, + 'target_bit_rate_mbps': round(target_video_bps / 1_000_000, 2), + 'size_bytes': size_bytes, + 'size_gb': round(size_bytes / (1024 ** 3), 3), + 'codec': codec, + 'width': width, + 'height': height, + } + except Exception: + return None + + +# --------------------------------------------------------------------------- +# Directory scanner +# --------------------------------------------------------------------------- + +def list_video_files(directory: Path, min_size_gb: float) -> list[dict]: + """ + Recursively walk *directory* and return video files larger than + *min_size_gb* gigabytes. + + Each entry is a dict with: path, name, size_bytes, size_gb. + Raises PermissionError if the root directory is inaccessible. + """ + min_bytes = min_size_gb * (1024 ** 3) + results: list[dict] = [] + + try: + for root, dirs, files in os.walk(directory): + dirs[:] = [d for d in dirs if not d.startswith('.')] + for fname in files: + if Path(fname).suffix.lower() in VIDEO_EXTENSIONS: + fpath = os.path.join(root, fname) + try: + fsize = os.path.getsize(fpath) + if fsize >= min_bytes: + results.append({ + 'path': fpath, + 'name': fname, + 'size_bytes': fsize, + 'size_gb': round(fsize / (1024 ** 3), 3), + }) + except OSError: + continue + except PermissionError as exc: + raise PermissionError(f"Cannot access directory: {exc}") from exc + + return results diff --git a/app/notify.py b/app/notify.py new file mode 100644 index 0000000..35d2b61 --- /dev/null +++ b/app/notify.py @@ -0,0 +1,329 @@ +""" +app/notify.py +============= +Email notification helper for compression job completion. + +Delivery uses SMTP settings stored in SQLite (via app.db). +If no SMTP settings have been configured, the send call returns an +informative error rather than silently failing. + +Public API +---------- + get_smtp_config() -> dict with all SMTP fields (safe for the UI) + send_completion_email(to, results, cancelled) -> (ok: bool, error: str) + +SMTP settings keys (stored in the 'settings' table) +---------------------------------------------------- + smtp_host — hostname or IP of the SMTP server + smtp_port — port number (str) + smtp_security — 'tls' (STARTTLS) | 'ssl' (SMTPS) | 'none' + smtp_user — login username (optional) + smtp_password — login password (optional, stored as-is) + smtp_from — From: address used in sent mail +""" + +import smtplib +import socket +import ssl +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.utils import formatdate, make_msgid + +from .db import get_setting + +# --------------------------------------------------------------------------- +# SMTP config helper +# --------------------------------------------------------------------------- + +def get_smtp_config() -> dict: + """ + Read SMTP settings from the database and return them as a dict. + The password field is replaced with a placeholder so this dict is + safe to serialise and send to the browser. + + Returns + ------- + { + host, port, security, user, from_addr, + password_set: bool (True if a password is stored) + } + """ + return { + 'host': get_setting('smtp_host') or '', + 'port': get_setting('smtp_port') or '587', + 'security': get_setting('smtp_security') or 'tls', + 'user': get_setting('smtp_user') or '', + 'from_addr': get_setting('smtp_from') or '', + 'password_set': bool(get_setting('smtp_password')), + } + + +def _load_smtp_config() -> dict: + """Load full config including the raw password (server-side only).""" + return { + 'host': get_setting('smtp_host') or '', + 'port': int(get_setting('smtp_port') or 587), + 'security': get_setting('smtp_security') or 'tls', + 'user': get_setting('smtp_user') or '', + 'password': get_setting('smtp_password') or '', + 'from_addr': get_setting('smtp_from') or '', + } + + +# --------------------------------------------------------------------------- +# Send helper +# --------------------------------------------------------------------------- + +def send_completion_email( + to_address: str, + results: list[dict], + cancelled: bool, +) -> tuple[bool, str]: + """ + Send a job-completion notification to *to_address* using the SMTP + settings stored in SQLite. + + Returns (success, error_message). + """ + if not to_address or '@' not in to_address: + return False, 'Invalid recipient email address' + + cfg = _load_smtp_config() + + if not cfg['host']: + return False, ( + 'No SMTP server configured. ' + 'Please add your SMTP settings in the ⚙ Settings panel.' + ) + if not cfg['from_addr']: + return False, ( + 'No From address configured. ' + 'Please add your SMTP settings in the ⚙ Settings panel.' + ) + + # ── Build message ───────────────────────────────────────────────────── + done_files = [r for r in results if r.get('status') == 'done'] + error_files = [r for r in results if r.get('status') == 'error'] + total = len(results) + hostname = socket.getfqdn() + + if cancelled: + subject = (f'VideoPress: compression cancelled ' + f'({len(done_files)}/{total} completed) on {hostname}') + elif error_files: + subject = (f'VideoPress: compression complete with ' + f'{len(error_files)} error(s) on {hostname}') + else: + subject = (f'VideoPress: compression complete — ' + f'{total} file(s) processed on {hostname}') + + msg = MIMEMultipart('alternative') + msg['Subject'] = subject + msg['From'] = cfg['from_addr'] + msg['To'] = to_address + msg['Date'] = formatdate(localtime=True) + msg['Message-ID'] = make_msgid(domain=hostname) + msg.attach(MIMEText( + _build_plain(hostname, cancelled, done_files, error_files, total), + 'plain', 'utf-8', + )) + msg.attach(MIMEText( + _build_html(hostname, subject, cancelled, done_files, error_files, total), + 'html', 'utf-8', + )) + + # ── Connect and send ────────────────────────────────────────────────── + try: + security = cfg['security'].lower() + host = cfg['host'] + port = cfg['port'] + + if security == 'ssl': + # SMTPS — wrap in SSL from the start (port 465 typically) + context = ssl.create_default_context() + server = smtplib.SMTP_SSL(host, port, context=context, timeout=15) + else: + # Plain or STARTTLS (port 587 typically) + server = smtplib.SMTP(host, port, timeout=15) + server.ehlo() + if security == 'tls': + context = ssl.create_default_context() + server.starttls(context=context) + server.ehlo() + + with server: + if cfg['user'] and cfg['password']: + server.login(cfg['user'], cfg['password']) + server.sendmail(cfg['from_addr'], [to_address], msg.as_bytes()) + + return True, '' + + except smtplib.SMTPAuthenticationError: + return False, ( + 'Authentication failed — check your username and password. ' + 'For Gmail/Google Workspace, use an App Password rather than ' + 'your account password.' + ) + except smtplib.SMTPConnectError as exc: + return False, ( + f'Could not connect to {host}:{port}. ' + f'Check the host, port, and security setting. ({exc})' + ) + except smtplib.SMTPRecipientsRefused as exc: + refused = ', '.join(exc.recipients.keys()) + return False, f'Recipient address rejected by server: {refused}' + except smtplib.SMTPSenderRefused as exc: + return False, ( + f'From address "{cfg["from_addr"]}" was rejected by the server. ' + f'Ensure it matches your authenticated account. ({exc.smtp_error.decode(errors="replace")})' + ) + except smtplib.SMTPException as exc: + return False, f'SMTP error: {exc}' + except ssl.SSLError as exc: + return False, ( + f'SSL/TLS error connecting to {host}:{port} — ' + f'try changing the Security setting. ({exc})' + ) + except TimeoutError: + return False, ( + f'Connection to {host}:{port} timed out. ' + f'Check the host and port, and that the server is reachable.' + ) + except OSError as exc: + return False, ( + f'Network error connecting to {host}:{port} — {exc}. ' + f'Check the hostname and that the server is reachable.' + ) + except Exception as exc: + return False, f'Unexpected error: {exc}' + + +# --------------------------------------------------------------------------- +# Email body builders +# --------------------------------------------------------------------------- + +def _build_plain(hostname, cancelled, done_files, error_files, total) -> str: + lines = [ + 'VideoPress Compression Report', + f'Host : {hostname}', + f'Status : {"Cancelled" if cancelled else "Complete"}', + f'Files : {len(done_files)} succeeded, {len(error_files)} failed, {total} total', + '', + ] + if done_files: + lines.append('Completed files:') + for r in done_files: + lines.append( + f" ✓ {r.get('filename','?')} " + f"({r.get('output_size_gb','?')} GB, " + f"-{r.get('reduction_pct','?')}%)" + ) + lines.append('') + if error_files: + lines.append('Failed files:') + for r in error_files: + lines.append( + f" ✗ {r.get('filename','?')} " + f"— {r.get('message','unknown error')}" + ) + lines.append('') + lines += ['—', 'Sent by VideoPress FFmpeg Compressor'] + return '\n'.join(lines) + + +def _build_html(hostname, subject, cancelled, done_files, error_files, total) -> str: + status_colour = ( + '#166534' if not cancelled and not error_files + else '#92400e' if cancelled + else '#991b1b' + ) + status_label = ( + 'Cancelled' if cancelled + else 'Complete ✓' if not error_files + else 'Complete with errors' + ) + + def file_rows(files, icon, bg): + rows = '' + for r in files: + detail = ( + f"{r.get('output_size_gb','?')} GB  ·  " + f"-{r.get('reduction_pct','?')}%" + if r.get('status') == 'done' + else r.get('message', 'unknown error') + ) + rows += ( + f'' + f'{icon}' + f'' + f'{r.get("filename","?")}' + f'{detail}' + f'' + ) + return rows + + done_rows = file_rows(done_files, '✅', '#f0fdf4') + error_rows = file_rows(error_files, '❌', '#fef2f2') + + error_cell = ( + f'
Failed
' + f'
' + f'{len(error_files)}
' + ) if error_files else '' + + done_section = ( + f'

Completed

' + f'' + f'{done_rows}
' + ) if done_files else '' + + error_section = ( + f'

Errors

' + f'' + f'{error_rows}
' + ) if error_files else '' + + return f""" + +{subject} + +
+
+ + + VideoPress + +
+
+

Compression Run Report

+

Host: {hostname}

+
+
+
Status
+
{status_label}
+
+
+
Total
+
{total}
+
+
+
Succeeded
+
{len(done_files)}
+
+ {error_cell} +
+ {done_section} + {error_section} +
+

Sent by VideoPress FFmpeg Compressor

+
+
+ +""" diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..5b2f04f --- /dev/null +++ b/app/routes.py @@ -0,0 +1,470 @@ +""" +app/routes.py +============= +All Flask route handlers. Registered on the app object via register_routes() +which is called by the application factory in app/__init__.py. + +Routes +------ + GET / → index page + GET /api/config → server configuration (media_root) + GET /api/browse?path=… → directory listing + POST /api/scan → scan for video files + POST /api/compress/start → start a compression job + GET /api/compress/progress/ → SSE progress stream + POST /api/compress/cancel/ → cancel a running job +""" + +import json +import time +import threading +from pathlib import Path + +from flask import Flask, Response, jsonify, render_template, request, stream_with_context + +from .config import MEDIA_ROOT, safe_path +from .db import get_all_settings, save_setting, delete_setting +from .media import get_video_info, list_video_files +from .jobs import active_jobs, job_lock, run_compression_job +from .notify import get_smtp_config, send_completion_email + + +def fmttime(seconds: float) -> str: + """Format *seconds* as M:SS or H:MM:SS.""" + s = int(seconds) + h = s // 3600 + m = (s % 3600) // 60 + sec = s % 60 + if h: + return f"{h}:{m:02d}:{sec:02d}" + return f"{m}:{sec:02d}" + + +def register_routes(app: Flask) -> None: + """Attach all routes to *app*.""" + + # ── UI ──────────────────────────────────────────────────────────────── + + @app.route('/') + def index(): + return render_template('index.html', media_root=str(MEDIA_ROOT)) + + # ── Config ──────────────────────────────────────────────────────────── + + @app.route('/api/config') + def api_config(): + """Return server-side settings the frontend needs at startup.""" + return jsonify({'media_root': str(MEDIA_ROOT)}) + + # ── SMTP settings ───────────────────────────────────────────────────── + + @app.route('/api/settings/smtp', methods=['GET']) + def smtp_settings_get(): + """ + Return current SMTP settings (password is never sent, only a flag + indicating whether one is stored). + """ + return jsonify(get_smtp_config()) + + @app.route('/api/settings/smtp', methods=['POST']) + def smtp_settings_save(): + """ + Save SMTP settings to SQLite. Only fields present in the request + body are updated; omitting 'password' leaves the stored password + unchanged (useful when the user edits other fields but doesn't want + to re-enter the password). + """ + data = request.get_json(silent=True) or {} + + # Fields whose DB key matches smtp_{field} exactly + for field in ('host', 'port', 'security'): + if field in data: + value = str(data[field]).strip() + if not value: + return jsonify({'error': f"'{field}' cannot be empty"}), 400 + save_setting(f'smtp_{field}', value) + + # from_addr is stored as 'smtp_from' (not 'smtp_from_addr') + if 'from_addr' in data: + value = str(data['from_addr']).strip() + if not value: + return jsonify({'error': "'from_addr' cannot be empty"}), 400 + save_setting('smtp_from', value) + + # Optional fields + if 'user' in data: + val = str(data['user']).strip() + if val: + save_setting('smtp_user', val) + else: + delete_setting('smtp_user') + + # Password: only update if a non-empty value is explicitly sent + if 'password' in data and str(data['password']).strip(): + save_setting('smtp_password', str(data['password']).strip()) + + return jsonify({'ok': True, 'config': get_smtp_config()}) + + @app.route('/api/settings/smtp/test', methods=['POST']) + def smtp_settings_test(): + """ + Send a test email using the currently saved SMTP settings. + Always returns HTTP 200 — SMTP failures are reported in the + JSON body as {ok: false, message: "..."} so the browser can + display the exact error without interference from proxies or + the browser's own error handling for 5xx responses. + """ + data = request.get_json(silent=True) or {} + test_to = data.get('to', '').strip() + + if not test_to or '@' not in test_to: + return jsonify({'ok': False, 'message': 'Please enter a valid recipient address.'}), 400 + + ok, err = send_completion_email( + to_address = test_to, + results = [{ + 'status': 'done', + 'filename': 'test_video.mp4', + 'output_size_gb': 1.2, + 'reduction_pct': 33, + }], + cancelled = False, + ) + + if ok: + return jsonify({'ok': True, 'message': f'Test email sent to {test_to}.'}) + + # Always 200 — the caller checks data.ok, not the HTTP status + return jsonify({'ok': False, 'message': err}) + + # ── Directory browser ───────────────────────────────────────────────── + + @app.route('/api/browse') + def browse_directory(): + raw = request.args.get('path', str(MEDIA_ROOT)) + try: + path = safe_path(raw) + except PermissionError as exc: + return jsonify({'error': str(exc)}), 403 + + if not path.exists(): + return jsonify({'error': 'Path does not exist'}), 404 + if not path.is_dir(): + return jsonify({'error': 'Not a directory'}), 400 + + try: + entries = [ + {'name': e.name, 'path': str(e), 'is_dir': e.is_dir()} + for e in sorted( + path.iterdir(), + key=lambda e: (not e.is_dir(), e.name.lower()), + ) + if not e.name.startswith('.') + ] + parent = str(path.parent) if path != MEDIA_ROOT else None + return jsonify({ + 'current': str(path), + 'parent': parent, + 'entries': entries, + 'media_root': str(MEDIA_ROOT), + }) + except PermissionError: + return jsonify({'error': 'Permission denied'}), 403 + + # ── File scanner ────────────────────────────────────────────────────── + + @app.route('/api/scan', methods=['POST']) + def scan_directory(): + data = request.get_json(silent=True) or {} + raw_dir = data.get('directory', '') + min_size_gb = float(data.get('min_size_gb', 1.0)) + + if not raw_dir: + return jsonify({'error': 'No directory provided'}), 400 + try: + directory = safe_path(raw_dir) + except PermissionError as exc: + return jsonify({'error': str(exc)}), 403 + if not directory.is_dir(): + return jsonify({'error': 'Invalid directory'}), 400 + + try: + files = list_video_files(directory, min_size_gb) + except PermissionError as exc: + return jsonify({'error': str(exc)}), 403 + + enriched = [] + for f in files: + info = get_video_info(f['path']) + if info: + f.update(info) + else: + # Rough fallback: assume a 90-minute feature film + bps = int((f['size_bytes'] * 8) / (90 * 60)) + f.update({ + 'bit_rate_bps': bps, + 'bit_rate_mbps': round(bps / 1_000_000, 2), + 'target_bit_rate_bps': max(bps // 3, 200_000), + 'target_bit_rate_mbps': round(max(bps // 3, 200_000) / 1_000_000, 2), + 'duration': 0, + 'codec': 'unknown', + 'width': 0, + 'height': 0, + }) + enriched.append(f) + + enriched.sort(key=lambda x: x['size_bytes'], reverse=True) + return jsonify({'files': enriched, 'count': len(enriched)}) + + # ── Compression — status snapshot (for reconnect/reload) ───────────── + + @app.route('/api/compress/status/') + def compression_status(job_id): + """ + Return a complete point-in-time snapshot of a job's state. + + This is used when the browser reconnects after losing the SSE stream + (page reload, tab backgrounded, network blip). The frontend replays + this snapshot to rebuild the full progress UI, then re-attaches the + live SSE stream from where it left off. + + Response shape + -------------- + { + job_id, status, total, current_index, + files: [ {path, name, ...original file info} ], + file_states: [ # one entry per file, index-aligned + { + status: 'waiting' | 'running' | 'done' | 'error', + percent: 0-100, + detail: str, # time elapsed / output size / error msg + filename, output, reduction_pct, output_size_gb (done only) + message (error only) + } + ], + done_count: int, + event_count: int # total events stored; SSE stream resumes from here + } + """ + with job_lock: + job = active_jobs.get(job_id) + if not job: + return jsonify({'error': 'Job not found'}), 404 + + with job['lock']: + events = list(job['events']) + status = job['status'] + total = job['total'] + current_index = job['current_index'] + files = job['files'] + + # Replay the event log to reconstruct per-file state + file_states = [ + {'status': 'waiting', 'percent': 0, 'detail': '', 'filename': f.get('name', '')} + for f in files + ] + done_count = 0 + + for evt in events: + t = evt.get('type') + idx = evt.get('index') + + if t == 'file_start' and idx is not None: + file_states[idx].update({ + 'status': 'running', + 'percent': 0, + 'detail': '', + 'filename': evt.get('filename', file_states[idx]['filename']), + 'output': evt.get('output', ''), + 'encoder': evt.get('encoder', ''), + }) + + elif t == 'progress' and idx is not None: + file_states[idx].update({ + 'status': 'running', + 'percent': evt.get('percent', 0), + 'detail': ( + f"{fmttime(evt.get('elapsed_secs',0))} / " + f"{fmttime(evt.get('duration_secs',0))}" + if evt.get('duration_secs', 0) > 0 else '' + ), + }) + + elif t == 'file_done' and idx is not None: + done_count += 1 + file_states[idx].update({ + 'status': 'done', + 'percent': 100, + 'detail': (f"{evt.get('output_size_gb','?')} GB " + f"saved {evt.get('reduction_pct','?')}%"), + 'filename': evt.get('filename', ''), + 'output': evt.get('output', ''), + 'reduction_pct': evt.get('reduction_pct', 0), + 'output_size_gb': evt.get('output_size_gb', 0), + }) + + elif t == 'file_error' and idx is not None: + file_states[idx].update({ + 'status': 'error', + 'percent': 0, + 'detail': evt.get('message', 'Unknown error'), + 'message': evt.get('message', ''), + }) + + return jsonify({ + 'job_id': job_id, + 'status': status, + 'total': total, + 'current_index': current_index, + 'done_count': done_count, + 'event_count': len(events), + 'files': files, + 'file_states': file_states, + }) + + # ── Compression — list active jobs (for page-load auto-reconnect) ───── + + @app.route('/api/compress/active') + def list_active_jobs(): + """ + Return a list of jobs that are currently running or recently finished. + The frontend calls this on page load to detect whether a job is in + progress and should be reconnected to. + """ + with job_lock: + jobs = list(active_jobs.values()) + + result = [] + for job in jobs: + with job['lock']: + result.append({ + 'job_id': job['id'], + 'status': job['status'], + 'total': job['total'], + 'current_index': job['current_index'], + }) + + # Most recent first + result.sort(key=lambda j: j['job_id'], reverse=True) + return jsonify({'jobs': result}) + + # ── Compression — start ─────────────────────────────────────────────── + + @app.route('/api/compress/start', methods=['POST']) + def start_compression(): + data = request.get_json(silent=True) or {} + files = data.get('files', []) + suffix = data.get('suffix', '_new') + notify_email = data.get('notify_email', '').strip() + + if not files: + return jsonify({'error': 'No files provided'}), 400 + + if notify_email and (len(notify_email) > 254 or '@' not in notify_email): + return jsonify({'error': 'Invalid notification email address'}), 400 + + for f in files: + try: + safe_path(f.get('path', '')) + except PermissionError as exc: + return jsonify({'error': str(exc)}), 403 + + job_id = f"job_{int(time.time() * 1000)}" + job = { + 'id': job_id, + 'files': files, + 'suffix': suffix, + 'notify_email': notify_email, + 'status': 'running', + 'current_index': 0, + 'total': len(files), + 'events': [], + 'process': None, + 'cancelled': False, + 'lock': threading.Lock(), + } + with job_lock: + active_jobs[job_id] = job + + threading.Thread( + target=run_compression_job, + args=(job_id,), + daemon=True, + ).start() + return jsonify({'job_id': job_id}) + + # ── Compression — SSE progress stream ───────────────────────────────── + + @app.route('/api/compress/progress/') + def compression_progress(job_id): + """ + Server-Sent Events stream for real-time job progress. + + Query param: ?from=N — start streaming from event index N (default 0). + On reconnect the client passes the last event index it saw so it only + receives new events, not a full replay of the history. + + Compatible with Gunicorn + gevent: time.sleep() yields the greenlet + rather than blocking a real OS thread. + """ + try: + start_from = int(request.args.get('from', 0)) + except (TypeError, ValueError): + start_from = 0 + + def event_stream(): + last_idx = start_from + while True: + with job_lock: + job = active_jobs.get(job_id) + if not job: + yield ( + f"data: {json.dumps({'type': 'error', 'message': 'Job not found'})}\n\n" + ) + return + + with job['lock']: + new_events = job['events'][last_idx:] + last_idx += len(new_events) + status = job['status'] + + for event in new_events: + yield f"data: {json.dumps(event)}\n\n" + + if status in ('done', 'cancelled', 'error') and not new_events: + break + + time.sleep(0.25) + + return Response( + stream_with_context(event_stream()), + mimetype='text/event-stream', + headers={ + 'Cache-Control': 'no-cache', + 'X-Accel-Buffering': 'no', + }, + ) + + # ── Compression — cancel ────────────────────────────────────────────── + + @app.route('/api/compress/cancel/', methods=['POST']) + def cancel_compression(job_id): + with job_lock: + job = active_jobs.get(job_id) + if not job: + return jsonify({'error': 'Job not found'}), 404 + + with job['lock']: + job['cancelled'] = True + proc = job.get('process') + + if proc and proc.poll() is None: + try: + proc.terminate() + time.sleep(1) + if proc.poll() is None: + proc.kill() + except Exception: + pass + + return jsonify({'status': 'cancellation requested'}) diff --git a/docker-compose.yml b/docker-compose.yml index e4f9aba..4ff7f93 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,13 +13,20 @@ services: videopress: - # build: - # context: . - # dockerfile: Dockerfile + build: + context: . + dockerfile: Dockerfile # ── Alternatively, use a pre-built image: ─────────────────────────────── - image: bmcgonag/videopress:latest + # image: videopress:latest + container_name: videopress + + # Run as UID:GID 1000:1000 (matches the 'appuser' created in the Dockerfile). + # This ensures the container can write to bind-mounted host directories + # that are owned by UID 1000. + user: "1000:1000" + restart: unless-stopped # ── Port mapping ───────────────────────────────────────────────────────── @@ -38,8 +45,19 @@ services: # You can also set MEDIA_HOST_PATH as an environment variable before # running docker compose: # export MEDIA_HOST_PATH=/mnt/nas/videos && docker compose up -d + # + # IMPORTANT — before first run, create the data directory on the HOST + # and give it to UID 1000 (the container's non-root user) so SQLite can + # write the settings database: + # + # mkdir -p ./data + # chown 1000:1000 ./data + # + # If you skip this step Docker will create ./data as root and the + # container will fail to start with "unable to open database file". volumes: - ${MEDIA_HOST_PATH:-/path/to/your/videos}:/media + - ./data:/data # ── Environment variables ───────────────────────────────────────────────── environment: @@ -47,6 +65,10 @@ services: # Must match the right-hand side of the volume mount above. MEDIA_ROOT: /media + # SQLite database path inside the container. + # Must match the right-hand side of the ./data:/data volume mount. + DB_PATH: /data/videopress.db + # TCP port Gunicorn listens on (must match EXPOSE in Dockerfile and # the right-hand side of the ports mapping above). PORT: 8080 @@ -57,15 +79,14 @@ services: # ── Resource limits (optional — uncomment to enable) ───────────────────── # Compressing large video files is CPU-intensive. Limits prevent the # container from starving other workloads on the host. - # Feel free to comment out this whole section if youw ant it to run full blast. - deploy: - resources: - limits: - cpus: '4' - memory: 2G - reservations: - cpus: '1' - memory: 512M + # deploy: + # resources: + # limits: + # cpus: '4' + # memory: 2G + # reservations: + # cpus: '1' + # memory: 512M # ── Health check ────────────────────────────────────────────────────────── healthcheck: diff --git a/run.py b/run.py new file mode 100644 index 0000000..472dc76 --- /dev/null +++ b/run.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +""" +run.py — Development server entry point. + +Usage: + python3 run.py [PORT] + +Do NOT use this in production — use Gunicorn via wsgi.py instead. +""" + +import sys +from app import create_app +from app.config import MEDIA_ROOT + +if __name__ == '__main__': + port = int(sys.argv[1]) if len(sys.argv) > 1 else 5000 + print(f"\n{'='*60}") + print(f" VideoPress — dev server http://localhost:{port}") + print(f" MEDIA_ROOT : {MEDIA_ROOT}") + print(f" WARNING : dev server only — use Gunicorn for production") + print(f"{'='*60}\n") + create_app().run(host='0.0.0.0', port=port, debug=False, threaded=True) diff --git a/start.sh b/start.sh old mode 100644 new mode 100755 index a2ad87f..4605f82 --- a/start.sh +++ b/start.sh @@ -50,11 +50,11 @@ if [[ "$MODE" == "prod" ]]; then echo " Press Ctrl+C to stop." echo "============================================================" echo "" - PORT="$PORT" exec gunicorn -c gunicorn.conf.py wsgi:app + PORT="$PORT" exec gunicorn -c gunicorn.conf.py wsgi:application else echo " WARNING: Dev server only — use --prod or Docker for production." echo " Starting Flask on http://localhost:${PORT}" echo "============================================================" echo "" - exec python3 app.py "$PORT" + exec python3 run.py "$PORT" fi diff --git a/static/css/main.css b/static/css/main.css index c23a460..4e7480d 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -1202,7 +1202,107 @@ body { color: rgba(245,245,242,0.65); } -/* ── Animations ─────────────────────────────────────────────── */ +/* ── Notification opt-in ────────────────────────────────────── */ +.notify-group { + display: flex; + flex-direction: column; + gap: var(--space-sm); + flex: 1; +} + +.notify-checkbox-row { + display: flex; + align-items: center; + gap: var(--space-sm); +} + +.notify-checkbox { + width: 18px; + height: 18px; + min-width: 18px; + min-height: 18px; + cursor: pointer; + accent-color: var(--accent); + flex-shrink: 0; +} + +.notify-label { + font-size: 0.9rem; + color: var(--text-primary); + cursor: pointer; + font-weight: 500; + line-height: 1.3; +} + +.notify-email-row { + display: flex; + flex-direction: column; + gap: var(--space-xs); + padding-left: 26px; /* indent under checkbox */ + animation: slide-down 180ms ease; +} + +.notify-email-row[hidden] { + display: none !important; +} + +@keyframes slide-down { + from { opacity: 0; transform: translateY(-6px); } + to { opacity: 1; transform: translateY(0); } +} + +.notify-email-label { + margin-bottom: 0; +} + +.notify-email-input { + max-width: 340px; +} + +.notify-divider { + width: 1px; + background: var(--border-base); + align-self: stretch; + margin: 0 var(--space-sm); + flex-shrink: 0; +} + +/* Notification send status shown in progress footer */ +.notify-status { + font-size: 0.85rem; + display: inline-flex; + align-items: center; + gap: var(--space-xs); + padding: var(--space-xs) var(--space-md); + border-radius: var(--radius-pill); + font-weight: 600; +} + +.notify-status[hidden] { + display: none !important; +} + +.notify-status.ok { + background: rgba(22, 101, 52, 0.10); + color: var(--text-success); + border: 1px solid rgba(22, 101, 52, 0.25); +} + +.notify-status.fail { + background: rgba(185, 28, 28, 0.10); + color: var(--text-danger); + border: 1px solid rgba(185, 28, 28, 0.25); +} + +[data-theme="dark"] .notify-status.ok { + background: rgba(134, 239, 172, 0.10); + border-color: rgba(134, 239, 172, 0.25); +} + +[data-theme="dark"] .notify-status.fail { + background: rgba(252, 165, 165, 0.10); + border-color: rgba(252, 165, 165, 0.25); +} @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } @@ -1212,6 +1312,60 @@ body { animation: pulse 1.8s ease infinite; } +/* ── Stream-lost banner ─────────────────────────────────────── */ +.stream-lost-banner { + display: flex; + align-items: center; + gap: var(--space-md); + flex-wrap: wrap; + background: rgba(180, 100, 0, 0.10); + border: 1.5px solid rgba(180, 100, 0, 0.35); + border-radius: var(--radius-md); + padding: var(--space-md) var(--space-lg); + margin-bottom: var(--space-lg); + color: #7a4500; +} + +[data-theme="dark"] .stream-lost-banner { + background: rgba(251, 191, 36, 0.10); + border-color: rgba(251, 191, 36, 0.30); + color: #fbbf24; +} + +.stream-lost-banner[hidden] { + display: none !important; +} + +.banner-icon { + font-size: 1.2rem; + flex-shrink: 0; +} + +.banner-text { + flex: 1; + font-size: 0.88rem; + font-weight: 500; + line-height: 1.4; +} + +/* Reconnect button sits in the card title row */ +.card-title .reconnect-btn { + margin-left: auto; + font-size: 0.78rem; + padding: 5px 12px; + min-height: 32px; + animation: pulse-reconnect 1.8s ease infinite; +} + +.reconnect-btn[hidden] { + display: none !important; +} + +@keyframes pulse-reconnect { + 0%, 100% { border-color: var(--btn-outline-border); } + 50% { border-color: var(--accent); color: var(--accent); } +} + /* ── Responsive ─────────────────────────────────────────────── */ @media (max-width: 768px) { .app-main { @@ -1258,3 +1412,123 @@ body { [data-theme="dark"] .file-table th { color: var(--text-primary); } + +/* ── Settings modal ─────────────────────────────────────────── */ +.settings-panel { + max-width: 560px; + max-height: 90vh; + display: flex; + flex-direction: column; +} + +.settings-body { + flex: 1; + overflow-y: auto; + padding: var(--space-lg) var(--space-xl); +} + +.settings-body::-webkit-scrollbar { width: 6px; } +.settings-body::-webkit-scrollbar-track { background: var(--bg-card2); } +.settings-body::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; } + +.settings-intro { + font-size: 0.85rem; + color: var(--text-muted); + margin-bottom: var(--space-lg); + line-height: 1.5; +} + +.settings-grid { + display: flex; + flex-direction: column; + gap: var(--space-lg); +} + +.settings-row-2 { + display: grid; + grid-template-columns: 100px 1fr; + gap: var(--space-md); +} + +.settings-divider-above { + border-top: 1px solid var(--border-base); + padding-top: var(--space-lg); + margin-top: var(--space-sm); +} + +.settings-save-status { + font-size: 0.82rem; + text-align: center; + min-height: 1.4em; + padding: var(--space-xs) var(--space-xl) var(--space-md); + color: var(--text-muted); +} + +.settings-test-result { + min-height: 1.4em; +} + +/* Password row with toggle */ +.password-row { + display: flex; + gap: var(--space-sm); + align-items: center; +} + +.password-row .text-input { flex: 1; } + +.btn-icon-inline { + width: 40px; + height: 40px; + min-width: 40px; + min-height: 40px; + border: 1.5px solid var(--border-input); + border-radius: var(--radius-md); + background: var(--bg-input); + color: var(--text-muted); + font-size: 1rem; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + transition: background var(--transition-fast), border-color var(--transition-fast); + flex-shrink: 0; +} + +.btn-icon-inline:hover { + background: var(--bg-row-alt); + border-color: var(--border-strong); +} + +/* Select input to match text-input style */ +.select-input { + cursor: pointer; +} + +/* Inline button link (used in hint text) */ +.btn-link { + background: none; + border: none; + padding: 0; + color: var(--text-link); + font: inherit; + font-size: inherit; + cursor: pointer; + text-decoration: underline; + text-underline-offset: 2px; +} + +.btn-link:hover { + color: var(--accent); +} + +/* Settings save status colours */ +.settings-save-status.ok { color: var(--text-success); } +.settings-save-status.fail { color: var(--text-danger); } + +/* SMTP not configured warning on the notify row */ +.smtp-warn { + color: var(--accent); + font-size: 0.78rem; + font-weight: 600; +} diff --git a/static/js/app.js b/static/js/app.js index d9602c2..377bcb8 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1,705 +1,38 @@ /** - * VideoPress — Frontend Application - * Handles all UI interactions, API calls, and SSE progress streaming. - * WCAG 2.2 compliant, fully functional, no stubs. + * app.js — VideoPress entry point + * -------------------------------- + * Imports every feature module and calls its init function. + * No application logic lives here — this file is intentionally thin. + * + * Module layout + * ------------- + * utils.js Pure helpers: esc(), fmtTime(), pad() + * state.js Shared state object, DOM refs (els), announce() + * theme.js Dark / light mode toggle + * browser.js Server-side directory browser modal + * scan.js /api/scan, file selection table, select-all controls + * progress.js Progress bars, results card, stream-lost banner + * stream.js SSE stream, reconnect, snapshot restore (applySnapshot) + * compress.js Start / cancel / restart compression, notification opt-in + * session.js Page-load restore via /api/compress/active + * settings.js SMTP email settings modal */ 'use strict'; -// ─── State ────────────────────────────────────────────────────────────────── -const state = { - scannedFiles: [], // enriched file objects from API - selectedPaths: new Set(), // paths of selected files - currentJobId: null, - eventSource: null, - compressionResults: [], - browserPath: '/', -}; +import { initTheme } from './modules/theme.js'; +import { initBrowser } from './modules/browser.js'; +import { initScan } from './modules/scan.js'; +import { initStreamControls } from './modules/stream.js'; +import { initCompress } from './modules/compress.js'; +import { tryRestoreSession } from './modules/session.js'; +import { initSettings } from './modules/settings.js'; -// ─── DOM References ────────────────────────────────────────────────────────── -const $ = (id) => document.getElementById(id); - -const els = { - // Config section - dirInput: $('dir-input'), - browseBtn: $('browse-btn'), - minSizeInput: $('min-size-input'), - suffixInput: $('suffix-input'), - scanBtn: $('scan-btn'), - scanStatus: $('scan-status'), - - // Browser modal - browserModal: $('browser-modal'), - browserList: $('browser-list'), - browserPath: $('browser-current-path'), - closeBrowser: $('close-browser'), - browserCancel: $('browser-cancel'), - browserSelect: $('browser-select'), - - // Files section - sectionFiles: $('section-files'), - selectAllBtn: $('select-all-btn'), - deselectAllBtn: $('deselect-all-btn'), - selectionSummary: $('selection-summary'), - fileTbody: $('file-tbody'), - compressBtn: $('compress-btn'), - - // Progress section - sectionProgress: $('section-progress'), - progTotal: $('prog-total'), - progDone: $('prog-done'), - progStatus: $('prog-status'), - overallBar: $('overall-bar'), - overallBarFill: $('overall-bar-fill'), - overallPct: $('overall-pct'), - fileProgressList: $('file-progress-list'), - cancelBtn: $('cancel-btn'), - - // Results - sectionResults: $('section-results'), - resultsContent: $('results-content'), - restartBtn: $('restart-btn'), - - // Theme - themeToggle: $('theme-toggle'), - themeIcon: $('theme-icon'), - - // Screen reader announce - srAnnounce: $('sr-announce'), -}; - -// ─── Accessibility Helper ───────────────────────────────────────────────────── -function announce(msg) { - els.srAnnounce.textContent = ''; - requestAnimationFrame(() => { - els.srAnnounce.textContent = msg; - }); -} - -// ─── Theme Management ───────────────────────────────────────────────────────── -function initTheme() { - const saved = localStorage.getItem('vp-theme'); - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - const theme = saved || (prefersDark ? 'dark' : 'light'); - applyTheme(theme); -} - -function applyTheme(theme) { - document.documentElement.setAttribute('data-theme', theme); - els.themeIcon.textContent = theme === 'dark' ? '☀' : '◑'; - els.themeToggle.setAttribute('aria-label', `Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`); - localStorage.setItem('vp-theme', theme); -} - -els.themeToggle.addEventListener('click', () => { - const current = document.documentElement.getAttribute('data-theme') || 'light'; - applyTheme(current === 'dark' ? 'light' : 'dark'); -}); - -// ─── Directory Browser ──────────────────────────────────────────────────────── -async function loadBrowserPath(path) { - els.browserList.innerHTML = '

Loading…

'; - els.browserPath.textContent = path; - - try { - const resp = await fetch(`/api/browse?path=${encodeURIComponent(path)}`); - if (!resp.ok) throw new Error((await resp.json()).error || 'Error loading directory'); - const data = await resp.json(); - - state.browserPath = data.current; - els.browserPath.textContent = data.current; - - let html = ''; - - // Parent directory link - if (data.parent !== null) { - html += ` - `; - } - - if (data.entries.length === 0 && !data.parent) { - html += '

No accessible directories found.

'; - } - - for (const entry of data.entries) { - if (!entry.is_dir) continue; - html += ` - `; - } - - if (html === '') { - html = '

No subdirectories found.

'; - } - - els.browserList.innerHTML = html; - - // Attach click events - els.browserList.querySelectorAll('.browser-item').forEach(item => { - item.addEventListener('click', () => loadBrowserPath(item.dataset.path)); - item.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - loadBrowserPath(item.dataset.path); - } - }); - }); - - } catch (err) { - els.browserList.innerHTML = ``; - } -} - -function openBrowserModal() { - els.browserModal.hidden = false; - document.body.style.overflow = 'hidden'; - loadBrowserPath(els.dirInput.value || '/'); - // Focus trap - els.closeBrowser.focus(); - announce('Directory browser opened'); -} - -function closeBrowserModal() { - els.browserModal.hidden = true; - document.body.style.overflow = ''; - els.browseBtn.focus(); - announce('Directory browser closed'); -} - -els.browseBtn.addEventListener('click', openBrowserModal); -els.closeBrowser.addEventListener('click', closeBrowserModal); -els.browserCancel.addEventListener('click', closeBrowserModal); - -els.browserSelect.addEventListener('click', () => { - els.dirInput.value = state.browserPath; - closeBrowserModal(); - announce(`Directory selected: ${state.browserPath}`); -}); - -// Close modal on backdrop click -els.browserModal.addEventListener('click', (e) => { - if (e.target === els.browserModal) closeBrowserModal(); -}); - -// Keyboard: close on Escape -document.addEventListener('keydown', (e) => { - if (e.key === 'Escape' && !els.browserModal.hidden) { - closeBrowserModal(); - } -}); - -// ─── Scan for Files ─────────────────────────────────────────────────────────── -els.scanBtn.addEventListener('click', async () => { - const directory = els.dirInput.value.trim(); - const minSize = parseFloat(els.minSizeInput.value); - - if (!directory) { - showScanStatus('Please enter a directory path.', 'error'); - els.dirInput.focus(); - return; - } - if (isNaN(minSize) || minSize <= 0) { - showScanStatus('Please enter a valid minimum size greater than 0.', 'error'); - els.minSizeInput.focus(); - return; - } - - els.scanBtn.disabled = true; - els.scanBtn.textContent = '⟳ Scanning…'; - showScanStatus('Scanning directory, please wait…', 'info'); - announce('Scanning directory for video files, please wait.'); - - // Hide previous results - els.sectionFiles.hidden = true; - - try { - const resp = await fetch('/api/scan', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ directory, min_size_gb: minSize }), - }); - - const data = await resp.json(); - - if (!resp.ok) { - showScanStatus(`Error: ${data.error}`, 'error'); - announce(`Scan failed: ${data.error}`); - return; - } - - state.scannedFiles = data.files; - state.selectedPaths.clear(); - - if (data.files.length === 0) { - showScanStatus( - `No video files larger than ${minSize} GB found in that directory.`, - 'warn' - ); - announce('No video files found matching your criteria.'); - return; - } - - showScanStatus(`Found ${data.files.length} file(s).`, 'success'); - announce(`Scan complete. Found ${data.files.length} video files.`); - renderFileTable(data.files); - els.sectionFiles.hidden = false; - els.sectionFiles.scrollIntoView({ behavior: 'smooth', block: 'start' }); - - } catch (err) { - showScanStatus(`Network error: ${err.message}`, 'error'); - announce(`Scan error: ${err.message}`); - } finally { - els.scanBtn.disabled = false; - els.scanBtn.innerHTML = ' Scan for Files'; - } -}); - -function showScanStatus(msg, type) { - els.scanStatus.textContent = msg; - els.scanStatus.style.color = type === 'error' ? 'var(--text-danger)' - : type === 'success' ? 'var(--text-success)' - : type === 'warn' ? 'var(--accent)' - : 'var(--text-muted)'; -} - -// ─── File Table Rendering ───────────────────────────────────────────────────── -function renderFileTable(files) { - let html = ''; - files.forEach((f, idx) => { - const sizeFmt = f.size_gb.toFixed(3); - const curBitrate = f.bit_rate_mbps ? `${f.bit_rate_mbps} Mbps` : 'Unknown'; - const tgtBitrate = f.target_bit_rate_mbps ? `${f.target_bit_rate_mbps} Mbps` : '—'; - const codec = (f.codec || 'unknown').toLowerCase(); - const pathDir = f.path.replace(f.name, ''); - - // Normalise codec label and pick a CSS modifier for the badge colour - const isHevc = ['hevc', 'h265', 'x265'].includes(codec); - const isH264 = ['h264', 'avc', 'x264'].includes(codec); - const codecLabel = isHevc ? 'H.265 / HEVC' - : isH264 ? 'H.264 / AVC' - : codec.toUpperCase(); - const codecMod = isHevc ? 'hevc' : isH264 ? 'h264' : ''; - - html += ` - - - - - - - - - ${sizeFmt} GB - - - ${escHtml(curBitrate)} - - - ${escHtml(tgtBitrate)} - - - ${escHtml(codecLabel)} - - `; - }); - - els.fileTbody.innerHTML = html; - - // Attach change events - els.fileTbody.querySelectorAll('.file-checkbox').forEach(chk => { - chk.addEventListener('change', () => { - const path = chk.dataset.path; - const row = chk.closest('tr'); - if (chk.checked) { - state.selectedPaths.add(path); - row.classList.add('selected'); - } else { - state.selectedPaths.delete(path); - row.classList.remove('selected'); - } - updateSelectionUI(); - }); - }); - - updateSelectionUI(); -} - -function updateSelectionUI() { - const total = state.scannedFiles.length; - const sel = state.selectedPaths.size; - els.selectionSummary.textContent = `${sel} of ${total} selected`; - els.compressBtn.disabled = sel === 0; -} - -els.selectAllBtn.addEventListener('click', () => { - els.fileTbody.querySelectorAll('.file-checkbox').forEach(chk => { - chk.checked = true; - state.selectedPaths.add(chk.dataset.path); - chk.closest('tr').classList.add('selected'); - }); - updateSelectionUI(); - announce(`All ${state.scannedFiles.length} files selected.`); -}); - -els.deselectAllBtn.addEventListener('click', () => { - els.fileTbody.querySelectorAll('.file-checkbox').forEach(chk => { - chk.checked = false; - chk.closest('tr').classList.remove('selected'); - }); - state.selectedPaths.clear(); - updateSelectionUI(); - announce('All files deselected.'); -}); - -// ─── Start Compression ──────────────────────────────────────────────────────── -els.compressBtn.addEventListener('click', async () => { - const selectedFiles = state.scannedFiles.filter(f => state.selectedPaths.has(f.path)); - if (selectedFiles.length === 0) return; - - const suffix = els.suffixInput.value.trim() || '_new'; - - const payload = { - files: selectedFiles.map(f => ({ - path: f.path, - size_bytes: f.size_bytes, - target_bit_rate_bps: f.target_bit_rate_bps || 1000000, - codec: f.codec || 'unknown', - })), - suffix, - }; - - els.compressBtn.disabled = true; - els.compressBtn.textContent = 'Starting…'; - - try { - const resp = await fetch('/api/compress/start', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload), - }); - - const data = await resp.json(); - - if (!resp.ok) { - alert(`Failed to start compression: ${data.error}`); - els.compressBtn.disabled = false; - els.compressBtn.innerHTML = ' Compress Selected Files'; - return; - } - - state.currentJobId = data.job_id; - state.compressionResults = []; - - // Show progress section - setupProgressSection(selectedFiles); - els.sectionProgress.hidden = false; - els.sectionProgress.scrollIntoView({ behavior: 'smooth', block: 'start' }); - announce(`Compression started for ${selectedFiles.length} file(s).`); - - // Start SSE stream - startProgressStream(data.job_id, selectedFiles); - - } catch (err) { - alert(`Error: ${err.message}`); - els.compressBtn.disabled = false; - els.compressBtn.innerHTML = ' Compress Selected Files'; - } -}); - -// ─── Progress Setup ─────────────────────────────────────────────────────────── -function setupProgressSection(files) { - els.progTotal.textContent = files.length; - els.progDone.textContent = '0'; - els.progStatus.textContent = 'Running'; - setOverallProgress(0); - - // Create per-file items - let html = ''; - files.forEach((f, idx) => { - html += ` -
-
- ${escHtml(f.name)} - Waiting -
-
-
-
-
- -
-
-
`; - }); - - els.fileProgressList.innerHTML = html; -} - -function setOverallProgress(pct) { - const p = Math.round(pct); - els.overallBarFill.style.width = `${p}%`; - els.overallBar.setAttribute('aria-valuenow', p); - els.overallPct.textContent = `${p}%`; -} - -function updateFileProgress(idx, pct, statusClass, statusText, detail, detailClass) { - const fill = $(`fpfill-${idx}`); - const bar = $(`fpbar-${idx}`); - const pctEl = $(`fppct-${idx}`); - const status = $(`fps-${idx}`); - const item = $(`fpi-${idx}`); - const det = $(`fpdetail-${idx}`); - - if (!fill) return; - - const p = Math.round(pct); - fill.style.width = `${p}%`; - bar.setAttribute('aria-valuenow', p); - pctEl.textContent = `${p}%`; - - status.className = `fp-status ${statusClass}`; - status.textContent = statusText; - - item.className = `file-progress-item ${statusClass}`; - - // Toggle animation on bar fill - fill.classList.toggle('active', statusClass === 'running'); - - if (detail !== undefined) { - det.textContent = detail; - det.className = `fp-detail ${detailClass || ''}`; - } -} - -// ─── SSE Stream Handling ────────────────────────────────────────────────────── -function startProgressStream(jobId, files) { - if (state.eventSource) { - state.eventSource.close(); - } - - state.eventSource = new EventSource(`/api/compress/progress/${jobId}`); - let doneCount = 0; - - state.eventSource.onmessage = (evt) => { - let data; - try { data = JSON.parse(evt.data); } - catch { return; } - - switch (data.type) { - case 'start': - els.progStatus.textContent = 'Running'; - break; - - case 'file_start': - updateFileProgress(data.index, 0, 'running', 'Compressing…', '', ''); - // Scroll to active item - const activeItem = $(`fpi-${data.index}`); - if (activeItem) { - activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - } - announce(`Compressing file ${data.index + 1} of ${data.total}: ${files[data.index]?.name || ''}`); - break; - - case 'progress': { - const pct = data.percent || 0; - let detail = ''; - if (data.elapsed_secs > 0 && data.duration_secs > 0) { - detail = `${fmtTime(data.elapsed_secs)} / ${fmtTime(data.duration_secs)}`; - } - updateFileProgress(data.index, pct, 'running', 'Compressing…', detail, ''); - - // Update overall progress - const overallPct = ((doneCount + (pct / 100)) / files.length) * 100; - setOverallProgress(overallPct); - break; - } - - case 'file_done': { - doneCount++; - els.progDone.textContent = doneCount; - const detail = data.reduction_pct - ? `Saved ${data.reduction_pct}% → ${data.output_size_gb} GB` - : 'Complete'; - updateFileProgress(data.index, 100, 'done', '✓ Done', detail, 'success'); - setOverallProgress((doneCount / files.length) * 100); - state.compressionResults.push({ ...data, status: 'done' }); - announce(`File complete: ${files[data.index]?.name}. Saved ${data.reduction_pct}%.`); - break; - } - - case 'file_error': { - doneCount++; - els.progDone.textContent = doneCount; - updateFileProgress(data.index, 0, 'error', '✗ Error', data.message, 'error'); - state.compressionResults.push({ ...data, status: 'error' }); - announce(`Error compressing file ${files[data.index]?.name}: ${data.message}`); - break; - } - - case 'done': - state.eventSource.close(); - els.progStatus.textContent = 'Complete'; - setOverallProgress(100); - els.cancelBtn.disabled = true; - announce('All compression operations complete.'); - showResults('done'); - break; - - case 'cancelled': - state.eventSource.close(); - els.progStatus.textContent = 'Cancelled'; - announce('Compression cancelled.'); - showResults('cancelled'); - break; - - case 'error': - state.eventSource.close(); - els.progStatus.textContent = 'Error'; - announce(`Compression error: ${data.message}`); - break; - } - }; - - state.eventSource.onerror = () => { - if (state.eventSource.readyState === EventSource.CLOSED) return; - console.error('SSE connection error'); - }; -} - -// ─── Cancel ─────────────────────────────────────────────────────────────────── -els.cancelBtn.addEventListener('click', async () => { - if (!state.currentJobId) return; - - const confirmed = window.confirm( - 'Cancel all compression operations? Any files currently being processed will be deleted.' - ); - if (!confirmed) return; - - els.cancelBtn.disabled = true; - els.cancelBtn.textContent = 'Cancelling…'; - - try { - await fetch(`/api/compress/cancel/${state.currentJobId}`, { method: 'POST' }); - announce('Cancellation requested.'); - } catch (err) { - console.error('Cancel error:', err); - } -}); - -// ─── Results ────────────────────────────────────────────────────────────────── -function showResults(finalStatus) { - const results = state.compressionResults; - let html = ''; - - if (finalStatus === 'cancelled') { - html += `

- Compression was cancelled. Any completed files are listed below. -

`; - } - - if (results.length === 0 && finalStatus === 'cancelled') { - html += '

No files were completed before cancellation.

'; - } - - results.forEach(r => { - if (r.status === 'done') { - html += ` -
- -
-
${escHtml(r.filename)}
-
→ ${escHtml(r.output || '')}
-
- -${r.reduction_pct}% -
`; - } else if (r.status === 'error') { - html += ` -
- -
-
${escHtml(r.filename)}
-
${escHtml(r.message)}
-
-
`; - } - }); - - if (html === '') { - html = '

No results to display.

'; - } - - els.resultsContent.innerHTML = html; - els.sectionResults.hidden = false; - els.sectionResults.scrollIntoView({ behavior: 'smooth', block: 'start' }); -} - -// ─── Restart ────────────────────────────────────────────────────────────────── -els.restartBtn.addEventListener('click', () => { - // Reset state - state.scannedFiles = []; - state.selectedPaths.clear(); - state.currentJobId = null; - state.compressionResults = []; - if (state.eventSource) { state.eventSource.close(); state.eventSource = null; } - - // Reset UI - els.sectionFiles.hidden = true; - els.sectionProgress.hidden = true; - els.sectionResults.hidden = true; - els.fileTbody.innerHTML = ''; - els.fileProgressList.innerHTML = ''; - els.scanStatus.textContent = ''; - els.compressBtn.innerHTML = ' Compress Selected Files'; - els.compressBtn.disabled = true; - els.cancelBtn.disabled = false; - els.cancelBtn.textContent = '✕ Cancel Compression'; - - // Scroll to top - document.getElementById('section-config').scrollIntoView({ behavior: 'smooth' }); - els.dirInput.focus(); - announce('Session reset. Ready to scan again.'); -}); - -// ─── Helpers ────────────────────────────────────────────────────────────────── -function escHtml(str) { - if (!str) return ''; - return String(str) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -function fmtTime(seconds) { - const s = Math.floor(seconds); - const h = Math.floor(s / 3600); - const m = Math.floor((s % 3600) / 60); - const sec = s % 60; - if (h > 0) return `${h}:${pad(m)}:${pad(sec)}`; - return `${m}:${pad(sec)}`; -} - -function pad(n) { - return String(n).padStart(2, '0'); -} - -// ─── Init ───────────────────────────────────────────────────────────────────── initTheme(); +initBrowser(); +initScan(); +initStreamControls(); +initCompress(); +initSettings(); + +tryRestoreSession(); diff --git a/static/js/modules/browser.js b/static/js/modules/browser.js new file mode 100644 index 0000000..01b4053 --- /dev/null +++ b/static/js/modules/browser.js @@ -0,0 +1,111 @@ +/** + * browser.js + * ---------- + * Server-side directory browser modal. + * + * Fetches directory listings from /api/browse and renders them inside the + * modal panel. The user navigates the server filesystem and selects a + * directory to populate the scan path input. + * + * Exports + * ------- + * initBrowser() — attach all event listeners; call once at startup + */ + +import { state, els, announce } from './state.js'; +import { esc } from './utils.js'; + +// ─── Internal helpers ───────────────────────────────────────────────────────── + +async function loadBrowserPath(path) { + els.browserList.innerHTML = + '

Loading…

'; + els.browserPath.textContent = path; + + try { + const resp = await fetch(`/api/browse?path=${encodeURIComponent(path)}`); + if (!resp.ok) throw new Error((await resp.json()).error || 'Error loading directory'); + const data = await resp.json(); + + state.browserPath = data.current; + els.browserPath.textContent = data.current; + + let html = ''; + + if (data.parent !== null) { + html += ` + `; + } + + for (const entry of data.entries) { + if (!entry.is_dir) continue; + html += ` + `; + } + + if (!html) html = '

No subdirectories found.

'; + els.browserList.innerHTML = html; + + els.browserList.querySelectorAll('.browser-item').forEach(btn => { + btn.addEventListener('click', () => loadBrowserPath(btn.dataset.path)); + btn.addEventListener('keydown', e => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + loadBrowserPath(btn.dataset.path); + } + }); + }); + + } catch (err) { + els.browserList.innerHTML = + ``; + } +} + +function openBrowser() { + els.browserModal.hidden = false; + document.body.style.overflow = 'hidden'; + loadBrowserPath(els.dirInput.value || '/'); + els.closeBrowser.focus(); + announce('Directory browser opened'); +} + +function closeBrowser() { + els.browserModal.hidden = true; + document.body.style.overflow = ''; + els.browseBtn.focus(); + announce('Directory browser closed'); +} + +// ─── Public init ───────────────────────────────────────────────────────────── + +/** + * Attach all event listeners for the directory browser modal. + * Call once during app initialisation. + */ +export function initBrowser() { + els.browseBtn.addEventListener('click', openBrowser); + els.closeBrowser.addEventListener('click', closeBrowser); + els.browserCancel.addEventListener('click', closeBrowser); + + els.browserModal.addEventListener('click', e => { + if (e.target === els.browserModal) closeBrowser(); + }); + + els.browserSelect.addEventListener('click', () => { + els.dirInput.value = state.browserPath; + closeBrowser(); + announce(`Directory selected: ${state.browserPath}`); + }); + + document.addEventListener('keydown', e => { + if (e.key === 'Escape' && !els.browserModal.hidden) closeBrowser(); + }); +} diff --git a/static/js/modules/compress.js b/static/js/modules/compress.js new file mode 100644 index 0000000..349f406 --- /dev/null +++ b/static/js/modules/compress.js @@ -0,0 +1,195 @@ +/** + * compress.js + * ----------- + * Compression job lifecycle: start, notification opt-in, cancel, and restart. + * + * Exports + * ------- + * initCompress() — attach all event listeners; call once at startup + */ + +import { state, els, announce } from './state.js'; +import { setupProgressSection, showResults } from './progress.js'; +import { startProgressStream } from './stream.js'; +import { smtpIsConfigured } from './settings.js'; + +// ─── Public init ───────────────────────────────────────────────────────────── + +/** + * Attach event listeners for: + * - Notification checkbox toggle + * - "Compress Selected Files" button + * - "Cancel Compression" button + * - "Start New Session" (restart) button + * + * Call once during app initialisation. + */ +export function initCompress() { + _initNotifyToggle(); + _initCompressButton(); + _initCancelButton(); + _initRestartButton(); +} + +// ─── Notification opt-in ───────────────────────────────────────────────────── + +function _initNotifyToggle() { + els.notifyChk.addEventListener('change', () => { + const show = els.notifyChk.checked; + els.notifyEmailRow.hidden = !show; + els.notifyEmail.setAttribute('aria-required', show ? 'true' : 'false'); + const warn = document.getElementById('smtp-not-configured-warn'); + if (show) { + els.notifyEmail.focus(); + if (warn) warn.hidden = smtpIsConfigured(); + } else { + els.notifyEmail.value = ''; + if (warn) warn.hidden = true; + } + }); +} + +// ─── Start compression ──────────────────────────────────────────────────────── + +function _initCompressButton() { + els.compressBtn.addEventListener('click', async () => { + const selectedFiles = state.scannedFiles.filter( + f => state.selectedPaths.has(f.path), + ); + if (!selectedFiles.length) return; + + const suffix = els.suffixInput.value.trim() || '_new'; + const notifyEmail = els.notifyChk.checked ? els.notifyEmail.value.trim() : ''; + + // Client-side email validation + if (els.notifyChk.checked) { + if (!notifyEmail) { + els.notifyEmail.setCustomValidity('Please enter your email address.'); + els.notifyEmail.reportValidity(); + return; + } + if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(notifyEmail)) { + els.notifyEmail.setCustomValidity('Please enter a valid email address.'); + els.notifyEmail.reportValidity(); + return; + } + els.notifyEmail.setCustomValidity(''); + } + + els.compressBtn.disabled = true; + els.compressBtn.textContent = 'Starting…'; + + try { + const resp = await fetch('/api/compress/start', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + files: selectedFiles.map(f => ({ + path: f.path, + size_bytes: f.size_bytes, + target_bit_rate_bps: f.target_bit_rate_bps || 1_000_000, + codec: f.codec || 'unknown', + })), + suffix, + notify_email: notifyEmail, + }), + }); + const data = await resp.json(); + + if (!resp.ok) { + alert(`Failed to start compression: ${data.error}`); + _resetCompressBtn(); + return; + } + + state.currentJobId = data.job_id; + state.seenEventCount = 0; + state.compressionResults = []; + sessionStorage.setItem('vp-job-id', data.job_id); + + setupProgressSection(selectedFiles); + els.sectionProgress.hidden = false; + els.sectionProgress.scrollIntoView({ behavior: 'smooth', block: 'start' }); + announce(`Compression started for ${selectedFiles.length} file(s).`); + startProgressStream(data.job_id, selectedFiles); + + } catch (err) { + alert(`Error: ${err.message}`); + _resetCompressBtn(); + } + }); +} + +function _resetCompressBtn() { + els.compressBtn.disabled = false; + els.compressBtn.innerHTML = + ' Compress Selected Files'; +} + +// ─── Cancel ─────────────────────────────────────────────────────────────────── + +function _initCancelButton() { + els.cancelBtn.addEventListener('click', async () => { + if (!state.currentJobId) return; + if (!confirm( + 'Cancel all compression operations? ' + + 'Any file currently being processed will be deleted.', + )) return; + + els.cancelBtn.disabled = true; + els.cancelBtn.textContent = 'Cancelling…'; + + try { + await fetch(`/api/compress/cancel/${state.currentJobId}`, { method: 'POST' }); + announce('Cancellation requested.'); + } catch (err) { + console.error('Cancel error:', err); + } + }); +} + +// ─── Restart (new session) ──────────────────────────────────────────────────── + +function _initRestartButton() { + els.restartBtn.addEventListener('click', () => { + // Clear state + state.scannedFiles = []; + state.selectedPaths.clear(); + state.currentJobId = null; + state.compressionResults = []; + state.seenEventCount = 0; + + if (state.eventSource) { + state.eventSource.close(); + state.eventSource = null; + } + if (state.reconnectTimer) { + clearTimeout(state.reconnectTimer); + state.reconnectTimer = null; + } + sessionStorage.removeItem('vp-job-id'); + + // Reset UI + els.sectionFiles.hidden = true; + els.sectionProgress.hidden = true; + els.sectionResults.hidden = true; + els.fileTbody.innerHTML = ''; + els.fileProgressList.innerHTML = ''; + els.scanStatus.textContent = ''; + els.notifyChk.checked = false; + els.notifyEmailRow.hidden = true; + els.notifyEmail.value = ''; + els.notifyStatus.hidden = true; + els.notifyStatus.textContent = ''; + els.streamLostBanner.hidden = true; + els.reconnectBtn.hidden = true; + els.cancelBtn.disabled = false; + els.cancelBtn.textContent = '✕ Cancel Compression'; + _resetCompressBtn(); + + document.getElementById('section-config') + .scrollIntoView({ behavior: 'smooth' }); + els.dirInput.focus(); + announce('Session reset. Ready to scan again.'); + }); +} diff --git a/static/js/modules/progress.js b/static/js/modules/progress.js new file mode 100644 index 0000000..6ac4424 --- /dev/null +++ b/static/js/modules/progress.js @@ -0,0 +1,172 @@ +/** + * progress.js + * ----------- + * Progress section DOM management: per-file bars, overall bar, and + * the final results summary. + * + * These functions are called by both stream.js (live SSE updates) and + * session.js (snapshot restore on reconnect / page reload). + * + * Exports + * ------- + * setupProgressSection(files) + * setOverallProgress(pct) + * updateFileProgress(idx, pct, statusClass, statusText, detail, detailClass) + * showStreamLost() + * hideStreamLost() + * showResults(finalStatus) + */ + +import { state, els, announce } from './state.js'; +import { esc } from './utils.js'; + +// ─── Progress section setup ─────────────────────────────────────────────────── + +/** + * Render the initial per-file progress items and reset counters. + * Called when a new compression job starts or when the DOM needs to be + * rebuilt after a full page reload. + * @param {Array} files — file objects from the job (name, path, …) + */ +export function setupProgressSection(files) { + els.progTotal.textContent = files.length; + els.progDone.textContent = '0'; + els.progStatus.textContent = 'Running'; + setOverallProgress(0); + + let html = ''; + files.forEach((f, idx) => { + html += ` +
+
+ ${esc(f.name)} + Waiting +
+
+
+
+
+ +
+
+
`; + }); + els.fileProgressList.innerHTML = html; +} + +// ─── Bar helpers ───────────────────────────────────────────────────────────── + +/** + * Update the overall progress bar. + * @param {number} pct 0–100 + */ +export function setOverallProgress(pct) { + const p = Math.min(100, Math.round(pct)); + els.overallBarFill.style.width = `${p}%`; + els.overallBar.setAttribute('aria-valuenow', p); + els.overallPct.textContent = `${p}%`; +} + +/** + * Update a single file's progress bar, status badge, and detail text. + * + * @param {number} idx — file index (0-based) + * @param {number} pct — 0–100 + * @param {string} statusClass — 'waiting' | 'running' | 'done' | 'error' + * @param {string} statusText — visible badge text + * @param {string} [detail] — optional sub-text (elapsed time, size saved…) + * @param {string} [detailClass]— optional class applied to the detail element + */ +export function updateFileProgress(idx, pct, statusClass, statusText, detail, detailClass) { + const fill = document.getElementById(`fpfill-${idx}`); + const bar = document.getElementById(`fpbar-${idx}`); + const pctEl = document.getElementById(`fppct-${idx}`); + const status = document.getElementById(`fps-${idx}`); + const item = document.getElementById(`fpi-${idx}`); + const det = document.getElementById(`fpdetail-${idx}`); + if (!fill) return; + + const p = Math.min(100, Math.round(pct)); + fill.style.width = `${p}%`; + bar.setAttribute('aria-valuenow', p); + pctEl.textContent = `${p}%`; + status.className = `fp-status ${statusClass}`; + status.textContent = statusText; + item.className = `file-progress-item ${statusClass}`; + fill.classList.toggle('active', statusClass === 'running'); + + if (detail !== undefined) { + det.textContent = detail; + det.className = `fp-detail ${detailClass || ''}`; + } +} + +// ─── Stream-lost banner ─────────────────────────────────────────────────────── + +/** Show the disconnection warning banner and reveal the Reconnect button. */ +export function showStreamLost() { + els.streamLostBanner.hidden = false; + els.reconnectBtn.hidden = false; + els.progStatus.textContent = 'Disconnected'; + announce('Live progress stream disconnected. Use Reconnect to resume.'); +} + +/** Hide the disconnection warning banner and Reconnect button. */ +export function hideStreamLost() { + els.streamLostBanner.hidden = true; + els.reconnectBtn.hidden = true; +} + +// ─── Results summary ───────────────────────────────────────────────────────── + +/** + * Render the Step 4 results card and scroll it into view. + * @param {'done'|'cancelled'} finalStatus + */ +export function showResults(finalStatus) { + const results = state.compressionResults; + let html = ''; + + if (finalStatus === 'cancelled') { + html += `

+ Compression was cancelled. Completed files are listed below.

`; + } + + if (!results.length && finalStatus === 'cancelled') { + html += '

No files were completed before cancellation.

'; + } + + results.forEach(r => { + if (r.status === 'done') { + html += ` +
+ +
+
${esc(r.filename)}
+
→ ${esc(r.output || '')}
+
+ -${r.reduction_pct}% +
`; + } else if (r.status === 'error') { + html += ` +
+ +
+
${esc(r.filename)}
+
+ ${esc(r.message)} +
+
+
`; + } + }); + + if (!html) html = '

No results to display.

'; + els.resultsContent.innerHTML = html; + els.sectionResults.hidden = false; + els.sectionResults.scrollIntoView({ behavior: 'smooth', block: 'start' }); +} diff --git a/static/js/modules/scan.js b/static/js/modules/scan.js new file mode 100644 index 0000000..3e96698 --- /dev/null +++ b/static/js/modules/scan.js @@ -0,0 +1,194 @@ +/** + * scan.js + * ------- + * Directory scan and file selection table. + * + * Handles the "Scan for Files" button, the /api/scan fetch, rendering the + * results table, and the select-all / deselect-all controls. + * + * Exports + * ------- + * initScan() — attach all event listeners; call once at startup + */ + +import { state, els, announce } from './state.js'; +import { esc } from './utils.js'; + +// ─── Status helper ──────────────────────────────────────────────────────────── + +function setScanStatus(msg, type) { + els.scanStatus.textContent = msg; + els.scanStatus.style.color = + type === 'error' ? 'var(--text-danger)' + : type === 'success' ? 'var(--text-success)' + : type === 'warn' ? 'var(--accent)' + : 'var(--text-muted)'; +} + +// ─── File table ─────────────────────────────────────────────────────────────── + +function updateSelectionUI() { + els.selectionSummary.textContent = + `${state.selectedPaths.size} of ${state.scannedFiles.length} selected`; + els.compressBtn.disabled = state.selectedPaths.size === 0; +} + +/** + * Build and inject the file selection table from the scan results. + * Attaches checkbox change handlers for each row. + * @param {Array} files — enriched file objects from /api/scan + */ +export function renderFileTable(files) { + let html = ''; + + files.forEach((f, idx) => { + const codec = (f.codec || 'unknown').toLowerCase(); + const isHevc = ['hevc', 'h265', 'x265'].includes(codec); + const isH264 = ['h264', 'avc', 'x264'].includes(codec); + const codecLabel = isHevc ? 'H.265 / HEVC' + : isH264 ? 'H.264 / AVC' + : codec.toUpperCase(); + const codecMod = isHevc ? 'hevc' : isH264 ? 'h264' : ''; + const pathDir = f.path.replace(f.name, ''); + + html += ` + + + + + + + + ${f.size_gb.toFixed(3)} GB + + + ${esc(f.bit_rate_mbps ? f.bit_rate_mbps + ' Mbps' : 'Unknown')} + + + + + ${esc(f.target_bit_rate_mbps ? f.target_bit_rate_mbps + ' Mbps' : '—')} + + + + + ${esc(codecLabel)} + + + `; + }); + + els.fileTbody.innerHTML = html; + + els.fileTbody.querySelectorAll('.file-checkbox').forEach(chk => { + chk.addEventListener('change', () => { + const row = chk.closest('tr'); + if (chk.checked) { + state.selectedPaths.add(chk.dataset.path); + row.classList.add('selected'); + } else { + state.selectedPaths.delete(chk.dataset.path); + row.classList.remove('selected'); + } + updateSelectionUI(); + }); + }); + + updateSelectionUI(); +} + +// ─── Public init ───────────────────────────────────────────────────────────── + +/** + * Attach event listeners for the scan button and file selection controls. + * Call once during app initialisation. + */ +export function initScan() { + // ── Scan button ────────────────────────────────────────────────────────── + els.scanBtn.addEventListener('click', async () => { + const directory = els.dirInput.value.trim(); + const minSize = parseFloat(els.minSizeInput.value); + + if (!directory) { + setScanStatus('Please enter a directory path.', 'error'); + els.dirInput.focus(); + return; + } + if (isNaN(minSize) || minSize <= 0) { + setScanStatus('Please enter a valid minimum size > 0.', 'error'); + els.minSizeInput.focus(); + return; + } + + els.scanBtn.disabled = true; + els.scanBtn.textContent = '⟳ Scanning…'; + setScanStatus('Scanning directory, please wait…', 'info'); + announce('Scanning directory for video files.'); + els.sectionFiles.hidden = true; + + try { + const resp = await fetch('/api/scan', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ directory, min_size_gb: minSize }), + }); + const data = await resp.json(); + + if (!resp.ok) { + setScanStatus(`Error: ${data.error}`, 'error'); + announce(`Scan failed: ${data.error}`); + return; + } + + state.scannedFiles = data.files; + state.selectedPaths.clear(); + + if (!data.files.length) { + setScanStatus(`No video files larger than ${minSize} GB found.`, 'warn'); + announce('No video files found matching your criteria.'); + return; + } + + setScanStatus(`Found ${data.files.length} file(s).`, 'success'); + announce(`Scan complete. Found ${data.files.length} video files.`); + renderFileTable(data.files); + els.sectionFiles.hidden = false; + els.sectionFiles.scrollIntoView({ behavior: 'smooth', block: 'start' }); + + } catch (err) { + setScanStatus(`Network error: ${err.message}`, 'error'); + } finally { + els.scanBtn.disabled = false; + els.scanBtn.innerHTML = + ' Scan for Files'; + } + }); + + // ── Select all ─────────────────────────────────────────────────────────── + els.selectAllBtn.addEventListener('click', () => { + els.fileTbody.querySelectorAll('.file-checkbox').forEach(chk => { + chk.checked = true; + state.selectedPaths.add(chk.dataset.path); + chk.closest('tr').classList.add('selected'); + }); + updateSelectionUI(); + announce(`All ${state.scannedFiles.length} files selected.`); + }); + + // ── Deselect all ───────────────────────────────────────────────────────── + els.deselectAllBtn.addEventListener('click', () => { + els.fileTbody.querySelectorAll('.file-checkbox').forEach(chk => { + chk.checked = false; + state.selectedPaths.delete(chk.dataset.path); + chk.closest('tr').classList.remove('selected'); + }); + updateSelectionUI(); + announce('All files deselected.'); + }); +} diff --git a/static/js/modules/session.js b/static/js/modules/session.js new file mode 100644 index 0000000..a8032f4 --- /dev/null +++ b/static/js/modules/session.js @@ -0,0 +1,59 @@ +/** + * session.js + * ---------- + * Page-load session restore. + * + * On every page load — including hard browser reloads (Ctrl+Shift+R) and + * opening the app in a new tab — asks the server whether a job is active, + * fetches its full snapshot, and reconnects the live SSE stream if needed. + * + * This does NOT depend on sessionStorage surviving the reload (though + * sessionStorage is still written as a fast secondary hint). + * + * Exports + * ------- + * tryRestoreSession() — call once at startup + */ + +import { announce } from './state.js'; +import { applySnapshot, startProgressStream } from './stream.js'; +import { showResults } from './progress.js'; + +/** + * Query the server for active/recent jobs and restore the UI if one is found. + * + * Strategy: + * 1. GET /api/compress/active — find the most recent running job (or any job) + * 2. GET /api/compress/status/ — fetch the full snapshot + * 3. applySnapshot() to rebuild all progress bars + * 4. If still running: re-attach the SSE stream + * 5. If done/cancelled: show the results card + */ +export async function tryRestoreSession() { + try { + const activeResp = await fetch('/api/compress/active'); + if (!activeResp.ok) return; + + const { jobs } = await activeResp.json(); + if (!jobs.length) return; + + // Prefer the most recent running job; fall back to any job + const candidate = jobs.find(j => j.status === 'running') || jobs[0]; + + const snapResp = await fetch(`/api/compress/status/${candidate.job_id}`); + if (!snapResp.ok) return; + + const snap = await snapResp.json(); + applySnapshot(snap); + announce('Active compression job restored.'); + + if (snap.status === 'running') { + startProgressStream(snap.job_id, snap.files); + } else if (snap.status === 'done' || snap.status === 'cancelled') { + showResults(snap.status); + sessionStorage.removeItem('vp-job-id'); + } + } catch { + // Server unreachable or no jobs — start fresh, no action needed + } +} diff --git a/static/js/modules/settings.js b/static/js/modules/settings.js new file mode 100644 index 0000000..86d4d98 --- /dev/null +++ b/static/js/modules/settings.js @@ -0,0 +1,260 @@ +/** + * settings.js + * ----------- + * SMTP email settings modal. + * + * Loads saved settings from the server on open, lets the user edit and + * save them, and sends a test email to verify the configuration works. + * + * Exports + * ------- + * initSettings() — wire up all listeners; call once at startup + * smtpIsConfigured() — returns true if the server has smtp_host saved + */ + +import { announce } from './state.js'; + +// ─── DOM refs (local to this module) ───────────────────────────────────────── + +const $ = id => document.getElementById(id); + +const modal = $('settings-modal'); +const openBtn = $('settings-btn'); +const openFromHint = $('open-settings-from-hint'); +const closeBtn = $('close-settings'); +const cancelBtn = $('settings-cancel'); +const saveBtn = $('settings-save'); +const saveStatus = $('settings-save-status'); + +const hostInput = $('smtp-host'); +const portInput = $('smtp-port'); +const securitySel = $('smtp-security'); +const fromInput = $('smtp-from'); +const userInput = $('smtp-user'); +const passwordInput = $('smtp-password'); +const passwordHint = $('smtp-password-hint'); +const togglePwBtn = $('toggle-password'); + +const testToInput = $('smtp-test-to'); +const testBtn = $('smtp-test-btn'); +const testResult = $('smtp-test-result'); + +// ─── Module-level state ─────────────────────────────────────────────────────── + +let _configured = false; // whether smtp_host is set on the server + +// ─── Public API ─────────────────────────────────────────────────────────────── + +/** + * Returns true if the server has an SMTP host configured. + * Used by compress.js to warn the user before they start a job with + * notifications enabled but no SMTP server set up. + */ +export function smtpIsConfigured() { + return _configured; +} + +/** + * Attach all event listeners for the settings modal. + * Call once during app initialisation. + */ +export function initSettings() { + openBtn.addEventListener('click', openSettings); + if (openFromHint) openFromHint.addEventListener('click', openSettings); + const openFromWarn = document.getElementById('open-settings-from-warn'); + if (openFromWarn) openFromWarn.addEventListener('click', openSettings); + + closeBtn.addEventListener('click', closeSettings); + cancelBtn.addEventListener('click', closeSettings); + modal.addEventListener('click', e => { if (e.target === modal) closeSettings(); }); + document.addEventListener('keydown', e => { + if (e.key === 'Escape' && !modal.hidden) closeSettings(); + }); + + saveBtn.addEventListener('click', saveSettings); + testBtn.addEventListener('click', sendTestEmail); + + // Password show/hide toggle + togglePwBtn.addEventListener('click', () => { + const isHidden = passwordInput.type === 'password'; + passwordInput.type = isHidden ? 'text' : 'password'; + togglePwBtn.setAttribute('aria-label', isHidden ? 'Hide password' : 'Show password'); + }); + + // Auto-fill port when security mode changes + securitySel.addEventListener('change', () => { + const presets = { tls: '587', ssl: '465', none: '25' }; + portInput.value = presets[securitySel.value] || portInput.value; + }); + + // Load current config silently at startup so smtpIsConfigured() works + _fetchConfig(false); +} + +// ─── Open / close ───────────────────────────────────────────────────────────── + +async function openSettings() { + modal.hidden = false; + document.body.style.overflow = 'hidden'; + clearStatus(); + await _fetchConfig(true); + closeBtn.focus(); + announce('SMTP settings panel opened'); +} + +function closeSettings() { + modal.hidden = true; + document.body.style.overflow = ''; + openBtn.focus(); + announce('SMTP settings panel closed'); +} + +// ─── Load settings from server ──────────────────────────────────────────────── + +async function _fetchConfig(populateForm) { + try { + const resp = await fetch('/api/settings/smtp'); + if (!resp.ok) return; + const cfg = await resp.json(); + + _configured = Boolean(cfg.host); + + if (!populateForm) return; + + hostInput.value = cfg.host || ''; + portInput.value = cfg.port || '587'; + fromInput.value = cfg.from_addr || ''; + userInput.value = cfg.user || ''; + passwordInput.value = ''; // never pre-fill passwords + + // Select the right security option + const opt = securitySel.querySelector(`option[value="${cfg.security || 'tls'}"]`); + if (opt) opt.selected = true; + + passwordHint.textContent = cfg.password_set + ? 'A password is saved. Enter a new value to replace it, or leave blank to keep it.' + : ''; + + } catch { + // Silently ignore — server may not be reachable during init + } +} + +// ─── Save ──────────────────────────────────────────────────────────────────── + +async function saveSettings() { + const host = hostInput.value.trim(); + const port = portInput.value.trim(); + const security = securitySel.value; + const from = fromInput.value.trim(); + const user = userInput.value.trim(); + const password = passwordInput.value; // not trimmed — passwords may have spaces + + if (!host) { + showStatus('SMTP server host is required.', 'fail'); + hostInput.focus(); + return; + } + if (!port || isNaN(Number(port))) { + showStatus('A valid port number is required.', 'fail'); + portInput.focus(); + return; + } + if (!from || !from.includes('@')) { + showStatus('A valid From address is required.', 'fail'); + fromInput.focus(); + return; + } + + saveBtn.disabled = true; + saveBtn.textContent = 'Saving…'; + clearStatus(); + + try { + const body = { host, port, security, from_addr: from, user }; + if (password) body.password = password; + + const resp = await fetch('/api/settings/smtp', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await resp.json(); + + if (!resp.ok) { + showStatus(`Error: ${data.error}`, 'fail'); + return; + } + + _configured = Boolean(data.config?.host); + passwordInput.value = ''; + passwordHint.textContent = + 'Password saved. Enter a new value to replace it, or leave blank to keep it.'; + showStatus('Settings saved successfully.', 'ok'); + announce('SMTP settings saved.'); + + } catch (err) { + showStatus(`Network error: ${err.message}`, 'fail'); + } finally { + saveBtn.disabled = false; + saveBtn.textContent = 'Save Settings'; + } +} + +// ─── Test email ─────────────────────────────────────────────────────────────── + +async function sendTestEmail() { + const to = testToInput.value.trim(); + if (!to || !to.includes('@')) { + setTestResult('Please enter a valid recipient address.', 'fail'); + testToInput.focus(); + return; + } + + testBtn.disabled = true; + testBtn.textContent = 'Sending…'; + setTestResult('Sending test email…', ''); + + try { + const resp = await fetch('/api/settings/smtp/test', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ to }), + }); + const data = await resp.json(); + + if (data.ok) { + setTestResult(`✓ ${data.message}`, 'ok'); + announce(`Test email sent to ${to}.`); + } else { + setTestResult(`✗ ${data.message}`, 'fail'); + announce(`Test email failed: ${data.message}`); + } + } catch (err) { + setTestResult(`Network error: ${err.message}`, 'fail'); + } finally { + testBtn.disabled = false; + testBtn.textContent = 'Send Test'; + } +} + +// ─── Helpers ───────────────────────────────────────────────────────────────── + +function showStatus(msg, type) { + saveStatus.textContent = msg; + saveStatus.className = `settings-save-status ${type}`; +} + +function clearStatus() { + saveStatus.textContent = ''; + saveStatus.className = 'settings-save-status'; + setTestResult('', ''); +} + +function setTestResult(msg, type) { + testResult.textContent = msg; + testResult.style.color = + type === 'ok' ? 'var(--text-success)' + : type === 'fail' ? 'var(--text-danger)' + : 'var(--text-muted)'; +} diff --git a/static/js/modules/state.js b/static/js/modules/state.js new file mode 100644 index 0000000..7bfc4b3 --- /dev/null +++ b/static/js/modules/state.js @@ -0,0 +1,119 @@ +/** + * state.js + * -------- + * Single shared application state object and all DOM element references. + * + * Centralising these here means every module imports the same live object — + * mutations made in one module are immediately visible to all others without + * any event bus or pub/sub layer. + * + * Also exports announce(), which every module uses to push messages to the + * ARIA live region for screen-reader users. + */ + +// ─── Shared mutable state ──────────────────────────────────────────────────── +export const state = { + /** Files returned by the last /api/scan call. */ + scannedFiles: [], + + /** Set of file paths the user has checked for compression. */ + selectedPaths: new Set(), + + /** job_id of the currently active or most-recently-seen compression job. */ + currentJobId: null, + + /** Active EventSource for the SSE progress stream. */ + eventSource: null, + + /** Per-file result objects accumulated during a compression run. */ + compressionResults: [], + + /** Current path shown in the server-side directory browser modal. */ + browserPath: '/', + + /** + * Index of the last SSE event we have processed. + * Passed as ?from=N when reconnecting so the server skips events + * we already applied to the UI. + */ + seenEventCount: 0, + + /** Handle returned by setTimeout for the auto-reconnect retry. */ + reconnectTimer: null, +}; + +// ─── DOM element references ─────────────────────────────────────────────────── +const $ = id => document.getElementById(id); + +export const els = { + // Step 1 — Configure source + dirInput: $('dir-input'), + browseBtn: $('browse-btn'), + minSizeInput: $('min-size-input'), + suffixInput: $('suffix-input'), + scanBtn: $('scan-btn'), + scanStatus: $('scan-status'), + + // Directory browser modal + browserModal: $('browser-modal'), + browserList: $('browser-list'), + browserPath: $('browser-current-path'), + closeBrowser: $('close-browser'), + browserCancel: $('browser-cancel'), + browserSelect: $('browser-select'), + + // Step 2 — File selection + sectionFiles: $('section-files'), + selectAllBtn: $('select-all-btn'), + deselectAllBtn: $('deselect-all-btn'), + selectionSummary: $('selection-summary'), + fileTbody: $('file-tbody'), + compressBtn: $('compress-btn'), + + // Email notification opt-in + notifyChk: $('notify-chk'), + notifyEmailRow: $('notify-email-row'), + notifyEmail: $('notify-email'), + + // Step 3 — Compression progress + sectionProgress: $('section-progress'), + progTotal: $('prog-total'), + progDone: $('prog-done'), + progStatus: $('prog-status'), + overallBar: $('overall-bar'), + overallBarFill: $('overall-bar-fill'), + overallPct: $('overall-pct'), + fileProgressList: $('file-progress-list'), + cancelBtn: $('cancel-btn'), + notifyStatus: $('notify-status'), + reconnectBtn: $('reconnect-btn'), + reconnectBtnBanner: $('reconnect-btn-banner'), + streamLostBanner: $('stream-lost-banner'), + + // Step 4 — Results + sectionResults: $('section-results'), + resultsContent: $('results-content'), + restartBtn: $('restart-btn'), + + // Header + themeToggle: $('theme-toggle'), + themeIcon: $('theme-icon'), + settingsBtn: $('settings-btn'), + + // Accessibility live region + srAnnounce: $('sr-announce'), +}; + +// ─── Screen-reader announcements ───────────────────────────────────────────── + +/** + * Push a message to the ARIA assertive live region. + * Clears first so repeated identical messages are still announced. + * @param {string} msg + */ +export function announce(msg) { + els.srAnnounce.textContent = ''; + requestAnimationFrame(() => { + els.srAnnounce.textContent = msg; + }); +} diff --git a/static/js/modules/stream.js b/static/js/modules/stream.js new file mode 100644 index 0000000..bf76935 --- /dev/null +++ b/static/js/modules/stream.js @@ -0,0 +1,276 @@ +/** + * stream.js + * --------- + * SSE progress stream management and reconnect / snapshot-restore logic. + * + * Exports + * ------- + * startProgressStream(jobId, files) — open (or re-open) the SSE connection + * reconnectToJob(jobId) — fetch snapshot then re-open stream + * applySnapshot(snap) — paint a server snapshot onto the UI + * initStreamControls() — wire up Reconnect buttons; call once + */ + +import { state, els, announce } from './state.js'; +import { fmtTime } from './utils.js'; +import { + setupProgressSection, + setOverallProgress, + updateFileProgress, + showStreamLost, + hideStreamLost, + showResults, +} from './progress.js'; + +// ─── SSE stream ─────────────────────────────────────────────────────────────── + +/** + * Open a Server-Sent Events connection for *jobId*. + * + * Resumes from state.seenEventCount so no events are replayed or skipped + * after a reconnect. doneCount is seeded from already-known results so + * the overall progress bar is correct on the first incoming event. + * + * @param {string} jobId + * @param {Array} files — file objects (need .name for announcements) + */ +export function startProgressStream(jobId, files) { + // Cancel any pending auto-reconnect timer + if (state.reconnectTimer) { + clearTimeout(state.reconnectTimer); + state.reconnectTimer = null; + } + // Close any stale connection + if (state.eventSource) { + state.eventSource.close(); + state.eventSource = null; + } + hideStreamLost(); + + state.eventSource = new EventSource( + `/api/compress/progress/${jobId}?from=${state.seenEventCount}`, + ); + + // Seed from results already recorded by applySnapshot (reconnect path) + let doneCount = state.compressionResults.filter( + r => r.status === 'done' || r.status === 'error', + ).length; + + state.eventSource.onmessage = evt => { + let data; + try { data = JSON.parse(evt.data); } catch { return; } + state.seenEventCount++; + + switch (data.type) { + + case 'start': + els.progStatus.textContent = 'Running'; + break; + + case 'file_start': + updateFileProgress(data.index, 0, 'running', 'Compressing…', '', ''); + document.getElementById(`fpi-${data.index}`) + ?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + announce( + `Compressing file ${data.index + 1} of ${data.total}: ` + + `${files[data.index]?.name || ''}`, + ); + break; + + case 'progress': { + const pct = data.percent || 0; + const detail = (data.elapsed_secs > 0 && data.duration_secs > 0) + ? `${fmtTime(data.elapsed_secs)} / ${fmtTime(data.duration_secs)}` : ''; + updateFileProgress(data.index, pct, 'running', 'Compressing…', detail, ''); + setOverallProgress(((doneCount + pct / 100) / files.length) * 100); + break; + } + + case 'file_done': { + doneCount++; + els.progDone.textContent = doneCount; + const detail = data.reduction_pct + ? `Saved ${data.reduction_pct}% → ${data.output_size_gb} GB` : 'Complete'; + updateFileProgress(data.index, 100, 'done', '✓ Done', detail, 'success'); + setOverallProgress((doneCount / files.length) * 100); + // Guard against replay on reconnect + if (!state.compressionResults.find( + r => r.index === data.index && r.status === 'done', + )) { + state.compressionResults.push({ ...data, status: 'done' }); + } + announce( + `File complete: ${files[data.index]?.name}. Saved ${data.reduction_pct}%.`, + ); + break; + } + + case 'file_error': { + doneCount++; + els.progDone.textContent = doneCount; + updateFileProgress(data.index, 0, 'error', '✗ Error', data.message, 'error'); + if (!state.compressionResults.find( + r => r.index === data.index && r.status === 'error', + )) { + state.compressionResults.push({ ...data, status: 'error' }); + } + announce(`Error: ${files[data.index]?.name}: ${data.message}`); + break; + } + + case 'notify': + els.notifyStatus.hidden = false; + els.notifyStatus.className = `notify-status ${data.success ? 'ok' : 'fail'}`; + els.notifyStatus.textContent = `✉ ${data.message}`; + announce(data.message); + break; + + case 'done': + state.eventSource.close(); + sessionStorage.removeItem('vp-job-id'); + els.progStatus.textContent = 'Complete'; + setOverallProgress(100); + els.cancelBtn.disabled = true; + announce('All compression operations complete.'); + showResults('done'); + break; + + case 'cancelled': + state.eventSource.close(); + sessionStorage.removeItem('vp-job-id'); + els.progStatus.textContent = 'Cancelled'; + announce('Compression cancelled.'); + showResults('cancelled'); + break; + + case 'error': + state.eventSource.close(); + els.progStatus.textContent = 'Error'; + announce(`Compression error: ${data.message}`); + break; + } + }; + + state.eventSource.onerror = () => { + // CLOSED means the stream ended cleanly (done/cancelled) — ignore. + if (!state.eventSource || state.eventSource.readyState === EventSource.CLOSED) return; + state.eventSource.close(); + state.eventSource = null; + showStreamLost(); + // Auto-retry after 5 s + state.reconnectTimer = setTimeout(() => { + if (state.currentJobId) reconnectToJob(state.currentJobId); + }, 5_000); + }; +} + +// ─── Reconnect ──────────────────────────────────────────────────────────────── + +/** + * Fetch a fresh status snapshot from the server, rebuild the progress UI to + * reflect everything that happened while disconnected, then re-open the SSE + * stream starting from the last event already processed. + * + * @param {string} jobId + */ +export async function reconnectToJob(jobId) { + if (state.reconnectTimer) { + clearTimeout(state.reconnectTimer); + state.reconnectTimer = null; + } + hideStreamLost(); + els.progStatus.textContent = 'Reconnecting…'; + announce('Reconnecting to compression job…'); + + try { + const resp = await fetch(`/api/compress/status/${jobId}`); + if (!resp.ok) throw new Error('Job no longer available on server.'); + const snap = await resp.json(); + + applySnapshot(snap); + + if (snap.status === 'done' || snap.status === 'cancelled') { + showResults(snap.status); + sessionStorage.removeItem('vp-job-id'); + } else { + startProgressStream(jobId, snap.files); + announce('Reconnected. Progress restored.'); + } + } catch (err) { + els.progStatus.textContent = 'Reconnect failed'; + showStreamLost(); + els.streamLostBanner.querySelector('.banner-text').textContent = + `Could not reconnect: ${err.message}`; + announce(`Reconnect failed: ${err.message}`); + } +} + +// ─── Snapshot restore ──────────────────────────────────────────────────────── + +/** + * Paint a server-supplied status snapshot onto the progress UI. + * + * Called by: + * - reconnectToJob() after a mid-session SSE drop + * - tryRestoreSession() on every page load to recover an active job + * + * @param {object} snap — response from GET /api/compress/status/ + */ +export function applySnapshot(snap) { + // Rebuild the per-file DOM if the page was reloaded and lost it + if (!document.getElementById('fpi-0')) { + setupProgressSection(snap.files); + } + + state.currentJobId = snap.job_id; + state.seenEventCount = snap.event_count; + sessionStorage.setItem('vp-job-id', snap.job_id); + + els.sectionProgress.hidden = false; + els.progTotal.textContent = snap.total; + els.progDone.textContent = snap.done_count; + els.progStatus.textContent = + snap.status === 'running' ? 'Running' + : snap.status === 'done' ? 'Complete' + : snap.status === 'cancelled' ? 'Cancelled' + : snap.status; + + // Restore each file bar from the snapshot's computed file_states + snap.file_states.forEach((fs, idx) => { + const statusClass = { done: 'done', error: 'error', running: 'running' }[fs.status] || 'waiting'; + const statusText = { done: '✓ Done', error: '✗ Error', running: 'Compressing…' }[fs.status] || 'Waiting'; + const detailClass = { done: 'success', error: 'error' }[fs.status] || ''; + updateFileProgress(idx, fs.percent || 0, statusClass, statusText, fs.detail || '', detailClass); + }); + + // Restore overall bar + const runningPct = snap.file_states.find(f => f.status === 'running')?.percent || 0; + const overall = snap.total > 0 + ? ((snap.done_count + runningPct / 100) / snap.total) * 100 : 0; + setOverallProgress(Math.min(overall, 100)); + + // Seed compressionResults so showResults() has data if job is already done + state.compressionResults = snap.file_states + .filter(fs => fs.status === 'done' || fs.status === 'error') + .map((fs, idx) => ({ ...fs, index: idx })); + + if (snap.status === 'done') { + els.cancelBtn.disabled = true; + setOverallProgress(100); + } +} + +// ─── Button wiring ──────────────────────────────────────────────────────────── + +/** + * Attach click handlers to both Reconnect buttons (title-bar and banner). + * Call once during app initialisation. + */ +export function initStreamControls() { + els.reconnectBtn.addEventListener('click', () => { + if (state.currentJobId) reconnectToJob(state.currentJobId); + }); + els.reconnectBtnBanner.addEventListener('click', () => { + if (state.currentJobId) reconnectToJob(state.currentJobId); + }); +} diff --git a/static/js/modules/theme.js b/static/js/modules/theme.js new file mode 100644 index 0000000..ad5253e --- /dev/null +++ b/static/js/modules/theme.js @@ -0,0 +1,46 @@ +/** + * theme.js + * -------- + * Dark / light theme management. + * + * Reads the user's saved preference from localStorage and falls back to the + * OS-level prefers-color-scheme media query. Writes back on every change + * so the choice persists across page loads. + * + * Exports + * ------- + * initTheme() — call once at startup; reads saved pref and applies it + * applyTheme() — apply a specific theme string ('dark' | 'light') + */ + +import { els } from './state.js'; + +/** + * Apply *theme* to the document and persist the choice. + * @param {'dark'|'light'} theme + */ +export function applyTheme(theme) { + document.documentElement.setAttribute('data-theme', theme); + els.themeIcon.textContent = theme === 'dark' ? '☀' : '◑'; + els.themeToggle.setAttribute( + 'aria-label', + `Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`, + ); + localStorage.setItem('vp-theme', theme); +} + +/** + * Read the saved theme preference (or detect from OS) and apply it. + * Attaches the toggle button's click listener. + * Call once during app initialisation. + */ +export function initTheme() { + const saved = localStorage.getItem('vp-theme'); + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + applyTheme(saved || (prefersDark ? 'dark' : 'light')); + + els.themeToggle.addEventListener('click', () => { + const current = document.documentElement.getAttribute('data-theme'); + applyTheme(current === 'dark' ? 'light' : 'dark'); + }); +} diff --git a/static/js/modules/utils.js b/static/js/modules/utils.js new file mode 100644 index 0000000..d0e1f15 --- /dev/null +++ b/static/js/modules/utils.js @@ -0,0 +1,45 @@ +/** + * utils.js + * -------- + * Pure utility functions with no DOM or state dependencies. + * Safe to import anywhere without side-effects. + */ + +/** + * Escape a string for safe insertion into HTML. + * @param {*} str + * @returns {string} + */ +export function esc(str) { + if (!str) return ''; + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +/** + * Format a duration in seconds as M:SS or H:MM:SS. + * @param {number} seconds + * @returns {string} + */ +export function fmtTime(seconds) { + const s = Math.floor(seconds); + const h = Math.floor(s / 3600); + const m = Math.floor((s % 3600) / 60); + const sec = s % 60; + return h > 0 + ? `${h}:${pad(m)}:${pad(sec)}` + : `${m}:${pad(sec)}`; +} + +/** + * Zero-pad a number to at least 2 digits. + * @param {number} n + * @returns {string} + */ +export function pad(n) { + return String(n).padStart(2, '0'); +} diff --git a/templates/index.html b/templates/index.html index c7b6b03..a45d760 100644 --- a/templates/index.html +++ b/templates/index.html @@ -21,6 +21,14 @@ VideoPress
+ . +

+ +
+ + + +
+ + +
@@ -229,6 +305,7 @@ +
@@ -246,6 +323,120 @@
+ + +