port huly stream video to staging (#8419)

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
Signed-off-by: denis-tingaikin <denis.tingajkin@xored.com>
Co-authored-by: Denis Tingaikin <denis.tingajkin@xored.com>
This commit is contained in:
Alexander Onnikov 2025-04-02 00:15:49 +07:00 committed by GitHub
parent 1a7a1bc717
commit f24ecc3515
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 496 additions and 261 deletions

View File

@ -13,9 +13,18 @@ importers:
'@anticrm/skillset':
specifier: ^0.6.0
version: 0.6.0
'@aws-sdk/client-s3':
specifier: ^3.738.0
version: 3.772.0
'@aws-sdk/client-ses':
specifier: ^3.738.0
version: 3.738.0
'@aws-sdk/lib-storage':
specifier: ^3.738.0
version: 3.772.0(@aws-sdk/client-s3@3.772.0)
'@aws-sdk/s3-request-presigner':
specifier: ^3.738.0
version: 3.772.0
'@elastic/elasticsearch':
specifier: ^7.17.14
version: 7.17.14
@ -1612,6 +1621,9 @@ importers:
emoji-regex:
specifier: ^10.1.0
version: 10.3.0
emojibase:
specifier: ^16.0.0
version: 16.0.0
esbuild:
specifier: ^0.24.2
version: 0.24.2
@ -1843,6 +1855,9 @@ importers:
pdfjs-dist:
specifier: 2.12.313
version: 2.12.313
plyr:
specifier: ^3.7.8
version: 3.7.8
png-chunks-extract:
specifier: ^1.0.0
version: 1.0.0
@ -2066,10 +2081,6 @@ packages:
'@aws-crypto/util@5.2.0':
resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
'@aws-sdk/client-s3@3.738.0':
resolution: {integrity: sha512-1Im/p5yfoV15ydVY+QlffsWQkQm7iGVI+3V9tCHEUT6SdmukYEpN3G8Y+lWofRBidxzUE2Xd+MbChCXfzLAoAg==}
engines: {node: '>=18.0.0'}
'@aws-sdk/client-s3@3.772.0':
resolution: {integrity: sha512-HQXlQIyyLp47h1/Hdjr36yK8/gsAAFX2vRzgDJhSRaz0vWZlWX07AJdYfrxapLUXfVU6DbBu3rwi2UGqM7ixqQ==}
engines: {node: '>=18.0.0'}
@ -2150,12 +2161,6 @@ packages:
resolution: {integrity: sha512-yHAT5Y2y0fnecSuWRUn8NMunKfDqFYhnOpGq8UyCEcwz9aXzibU0hqRIEm51qpR81hqo0GMFDH0EOmegZ/iW5w==}
engines: {node: '>=18.0.0'}
'@aws-sdk/lib-storage@3.738.0':
resolution: {integrity: sha512-YUBGp3k5Dg8RqHrllS89PjRiqpyIR3eKcQsCTM0bLzf3uRCjiCeSzlnl/co5W7Kxgc+eCnq0IitGYXR/mYFKeA==}
engines: {node: '>=18.0.0'}
peerDependencies:
'@aws-sdk/client-s3': ^3.738.0
'@aws-sdk/lib-storage@3.772.0':
resolution: {integrity: sha512-d59NJAHDa7mGf82ApOdIp1FsN1i7/VlNTk2t3NIepH/8FsMTzKPZQvbMzqE33ba4oik7y6QrDliYXoRXB/nZPg==}
engines: {node: '>=18.0.0'}
@ -2170,10 +2175,6 @@ packages:
resolution: {integrity: sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ==}
engines: {node: '>=18.0.0'}
'@aws-sdk/middleware-flexible-checksums@3.735.0':
resolution: {integrity: sha512-Tx7lYTPwQFRe/wQEHMR6Drh/S+X0ToAEq1Ava9QyxV1riwtepzRLojpNDELFb3YQVVYbX7FEiBMCJLMkmIIY+A==}
engines: {node: '>=18.0.0'}
'@aws-sdk/middleware-flexible-checksums@3.758.0':
resolution: {integrity: sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA==}
engines: {node: '>=18.0.0'}
@ -2198,10 +2199,6 @@ packages:
resolution: {integrity: sha512-zg0LjJa4v7fcLzn5QzZvtVS+qyvmsnu7oQnb86l6ckduZpWDCDC9+A0ZzcXTrxblPCJd3JqkoG1+Gzi4S4Ny/Q==}
engines: {node: '>=18.0.0'}
'@aws-sdk/middleware-sdk-s3@3.734.0':
resolution: {integrity: sha512-zeZPenDhkP/RXYMFG3exhNOe2Qukg2l2KpIjxq9o66meELiTULoIXjCmgPoWcM8zzrue06SBdTsaJDHfDl2vdA==}
engines: {node: '>=18.0.0'}
'@aws-sdk/middleware-sdk-s3@3.758.0':
resolution: {integrity: sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A==}
engines: {node: '>=18.0.0'}
@ -2230,18 +2227,10 @@ packages:
resolution: {integrity: sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==}
engines: {node: '>=18.0.0'}
'@aws-sdk/s3-request-presigner@3.738.0':
resolution: {integrity: sha512-cGENzkOxfAqrLfUdwYETDFiMOGjvicXV2EJe8sSH9Tw3wlm74WrIjZdw6ObHzm7AFnduHRNLVgX3KOtU7MlfCw==}
engines: {node: '>=18.0.0'}
'@aws-sdk/s3-request-presigner@3.772.0':
resolution: {integrity: sha512-y6OrwZXJyKaKw6suozQgc9AwKbZLjTu1hXe6sJ1rCyvhU171bWXjcdeux+Gw0iIX5wVdPQVHA/e5g9QYUaZOMw==}
engines: {node: '>=18.0.0'}
'@aws-sdk/signature-v4-multi-region@3.734.0':
resolution: {integrity: sha512-GSRP8UH30RIYkcpPILV4pWrKFjRmmNjtUd41HTKWde5GbjJvNYpxqFXw2aIJHjKTw/js3XEtGSNeTaQMVVt3CQ==}
engines: {node: '>=18.0.0'}
'@aws-sdk/signature-v4-multi-region@3.758.0':
resolution: {integrity: sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw==}
engines: {node: '>=18.0.0'}
@ -3859,7 +3848,7 @@ packages:
version: 0.0.0
'@rush-temp/card-resources@file:projects/card-resources.tgz':
resolution: {integrity: sha512-5apWhg2RHNKp8HUcu4H6FBItLJ4plUjTvHqnDitdSY9qeInCmpb0vik0U1F8AZmsES1W1PnZ5J/UJ8LN/tU3Pg==, tarball: file:projects/card-resources.tgz}
resolution: {integrity: sha512-FcEZHBMUGvv3IRmYXftC8F7YiJHWhaR+yFIUm1/ymdtDCmD5FQ+PyEpu7jMVoD24ahgRr87rZzji6uEw3jtCgw==, tarball: file:projects/card-resources.tgz}
version: 0.0.0
'@rush-temp/card@file:projects/card.tgz':
@ -4139,7 +4128,7 @@ packages:
version: 0.0.0
'@rush-temp/middleware@file:projects/middleware.tgz':
resolution: {integrity: sha512-d3bnkZYGycgZKQg4veVLwKczJdPhg65urbHhl9FJI3MV2izx8zCRu01frLW6zn61nYGW1fffugXfGoWBv4xxcA==, tarball: file:projects/middleware.tgz}
resolution: {integrity: sha512-kJq7mZtAvTPB+FJrAZsw09Qii5lri69Pyq6fr1HYpESs7BEHP7DbYqx4IWzcgq7jUJ4QOCyj3YcW/qejlxyDaQ==, tarball: file:projects/middleware.tgz}
version: 0.0.0
'@rush-temp/minio@file:projects/minio.tgz':
@ -4211,7 +4200,7 @@ packages:
version: 0.0.0
'@rush-temp/model-export@file:projects/model-export.tgz':
resolution: {integrity: sha512-PNegtj/2YMRt09wtM8e0PMh3hCXmlORZyIug/h0GZ1Y2b4bxHW4ZhNmdjtdv+UXpEP84GRs4Ab5DFZzqE/GMng==, tarball: file:projects/model-export.tgz}
resolution: {integrity: sha512-BvWGwqaQW9aByGimHCl9tUQwzJcflcvXwb7oKLJlERSDthQIbbmiwCbk2hoxoV3JnZvFAP+EIKqU8Gx4JmbBHQ==, tarball: file:projects/model-export.tgz}
version: 0.0.0
'@rush-temp/model-github@file:projects/model-github.tgz':
@ -4539,7 +4528,7 @@ packages:
version: 0.0.0
'@rush-temp/pod-datalake@file:projects/pod-datalake.tgz':
resolution: {integrity: sha512-ddwGsWoOitSul10+VuznzJ7CVuIHCuat68CjtwVzuNR0C+Neg+rN38Qv+83Tb/S98Ck39Ccziuz4ejw3Rqzm8w==, tarball: file:projects/pod-datalake.tgz}
resolution: {integrity: sha512-fPOgvrfdiJlH45D6XThXJ289JvBZTEXEwCyVcU+ap3VCcdOoMIgKTc9EmiFIryMEqqP0++w5LZ18Y6t8Q0qBbA==, tarball: file:projects/pod-datalake.tgz}
version: 0.0.0
'@rush-temp/pod-export@file:projects/pod-export.tgz':
@ -4687,7 +4676,7 @@ packages:
version: 0.0.0
'@rush-temp/recruit-resources@file:projects/recruit-resources.tgz':
resolution: {integrity: sha512-57h+96tqCbz2JuXP2uXAQU5SpWaZTqCLBrL6BTNwod9D28ZXxz7W03rWdL0eCVE7zFsm+rC13EMPg+e0lAuHkw==, tarball: file:projects/recruit-resources.tgz}
resolution: {integrity: sha512-zxPPlqTejoRW+M/nuH5nOCX9/Bl8s3wrlB3TYoTFX8Ej423+gjbUr0UbetcuckWYM+L38yI/asE6xO+BcG8NpA==, tarball: file:projects/recruit-resources.tgz}
version: 0.0.0
'@rush-temp/recruit@file:projects/recruit.tgz':
@ -4771,7 +4760,7 @@ packages:
version: 0.0.0
'@rush-temp/server-card-resources@file:projects/server-card-resources.tgz':
resolution: {integrity: sha512-sJdtOVlhTFawBXtSTzcx+Vs8NALsiqBfwAz9cqUBj45vobiSF97D987LPBgjhCARk2+qx8lZyUPPTUJkJdaLOQ==, tarball: file:projects/server-card-resources.tgz}
resolution: {integrity: sha512-x6WAVDpr6jvxJfo4XR+mkxbsKeZkp9YCndmJzLGEK/qaankXgnWax5n4pJhohJWbsE/qEuEYYqKl1h6fCMXzPA==, tarball: file:projects/server-card-resources.tgz}
version: 0.0.0
'@rush-temp/server-card@file:projects/server-card.tgz':
@ -5207,7 +5196,7 @@ packages:
version: 0.0.0
'@rush-temp/ui@file:projects/ui.tgz':
resolution: {integrity: sha512-tCUHrjveqVL6d3xRq8jtRIT7LdJUzDQFKavGeigF0kmaZxxSA9z5HBNCxszZu/vnK41+FVThVvwsExWnTr2ahA==, tarball: file:projects/ui.tgz}
resolution: {integrity: sha512-2BDBDtP2En69BA/YNcfdnAU8XmHD0+Lv8NYmePkZdynr9qf8fG+p/WruaPOy3WiP0tsFa1YqvTd6iK0iB8eBpg==, tarball: file:projects/ui.tgz}
version: 0.0.0
'@rush-temp/uploader-assets@file:projects/uploader-assets.tgz':
@ -5227,7 +5216,7 @@ packages:
version: 0.0.0
'@rush-temp/view-resources@file:projects/view-resources.tgz':
resolution: {integrity: sha512-QbVy5YO1NViW1f65Z7SqHS8XPq5IBtsy1uwVYZK6La5zBXjjJA3BgN960sYc1eWUoKIz3naElQUKzXMC1lYK9w==, tarball: file:projects/view-resources.tgz}
resolution: {integrity: sha512-7AFAEQlszTmRCzi9xfKegIhjzl76W8lLW4/LLygoH7a8JZPqoJhP1/l99QhNwa0tpUsXFTMg1EYMucFMrXhMdQ==, tarball: file:projects/view-resources.tgz}
version: 0.0.0
'@rush-temp/view@file:projects/view.tgz':
@ -5239,7 +5228,7 @@ packages:
version: 0.0.0
'@rush-temp/workbench-resources@file:projects/workbench-resources.tgz':
resolution: {integrity: sha512-VS0cGGm823IZf3WInBkTC/Tvpm6cWrofyw9q+WyZsUrHj4/eEx1bd7N9GGXZEPLIIkICVh2JtZLh1KemzD74XQ==, tarball: file:projects/workbench-resources.tgz}
resolution: {integrity: sha512-315FeO65RF4AACdkoe6rPuva+WRyhF48FvyYLOg558Te9tZqwEvKjwR/KWrgZPRkm8mcemw9xWBhirfO9Grkqw==, tarball: file:projects/workbench-resources.tgz}
version: 0.0.0
'@rush-temp/workbench@file:projects/workbench.tgz':
@ -7492,6 +7481,9 @@ packages:
peerDependencies:
webpack: ^5.1.0
core-js@3.41.0:
resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
@ -7635,6 +7627,9 @@ packages:
engines: {node: '>=4.0.0'}
hasBin: true
custom-event-polyfill@1.0.7:
resolution: {integrity: sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==}
cytoscape-cose-bilkent@4.1.0:
resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
peerDependencies:
@ -8227,6 +8222,10 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
emojibase@16.0.0:
resolution: {integrity: sha512-Nw2m7JLIO4Ou2X/yZPRNscHQXVbbr6SErjkJ7EooG7MbR3yDZszCv9KTizsXFc7yZl0n3WF+qUKIC/Lw6H9xaQ==}
engines: {node: '>=18.12.0'}
emojis-list@3.0.0:
resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
engines: {node: '>= 4'}
@ -10204,6 +10203,9 @@ packages:
resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==}
engines: {node: '>=8.9.0'}
loadjs@4.3.0:
resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==}
local-pkg@0.5.1:
resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
engines: {node: '>=14'}
@ -11286,6 +11288,9 @@ packages:
resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==}
engines: {node: '>=10.4.0'}
plyr@3.7.8:
resolution: {integrity: sha512-yG/EHDobwbB/uP+4Bm6eUpJ93f8xxHjjk2dYcD1Oqpe1EcuQl5tzzw9Oq+uVAzd2lkM11qZfydSiyIpiB8pgdA==}
png-chunks-extract@1.0.0:
resolution: {integrity: sha512-ZiVwF5EJ0DNZyzAqld8BP1qyJBaGOFaq9zl579qfbkcmOwWLLO4I9L8i2O4j3HkI6/35i0nKG2n+dZplxiT89Q==}
@ -11622,6 +11627,9 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
rangetouch@2.0.1:
resolution: {integrity: sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==}
raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
@ -12872,6 +12880,9 @@ packages:
url-parse@1.5.10:
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
url-polyfill@1.1.13:
resolution: {integrity: sha512-tXzkojrv2SujumYthZ/WjF7jaSfNhSXlYMpE5AYdL2I3D7DCeo+mch8KtW2rUuKjDg+3VXODXHVgipt8yGY/eQ==}
url-template@2.0.8:
resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==}
@ -13381,13 +13392,13 @@ snapshots:
dependencies:
'@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.734.0
tslib: 2.7.0
tslib: 2.8.1
'@aws-crypto/crc32c@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.734.0
tslib: 2.7.0
tslib: 2.8.1
'@aws-crypto/sha1-browser@5.2.0':
dependencies:
@ -13396,7 +13407,7 @@ snapshots:
'@aws-sdk/types': 3.734.0
'@aws-sdk/util-locate-window': 3.568.0
'@smithy/util-utf8': 2.3.0
tslib: 2.7.0
tslib: 2.8.1
'@aws-crypto/sha256-browser@5.2.0':
dependencies:
@ -13424,67 +13435,6 @@ snapshots:
'@smithy/util-utf8': 2.3.0
tslib: 2.7.0
'@aws-sdk/client-s3@3.738.0':
dependencies:
'@aws-crypto/sha1-browser': 5.2.0
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
'@aws-sdk/core': 3.734.0
'@aws-sdk/credential-provider-node': 3.738.0
'@aws-sdk/middleware-bucket-endpoint': 3.734.0
'@aws-sdk/middleware-expect-continue': 3.734.0
'@aws-sdk/middleware-flexible-checksums': 3.735.0
'@aws-sdk/middleware-host-header': 3.734.0
'@aws-sdk/middleware-location-constraint': 3.734.0
'@aws-sdk/middleware-logger': 3.734.0
'@aws-sdk/middleware-recursion-detection': 3.734.0
'@aws-sdk/middleware-sdk-s3': 3.734.0
'@aws-sdk/middleware-ssec': 3.734.0
'@aws-sdk/middleware-user-agent': 3.734.0
'@aws-sdk/region-config-resolver': 3.734.0
'@aws-sdk/signature-v4-multi-region': 3.734.0
'@aws-sdk/types': 3.734.0
'@aws-sdk/util-endpoints': 3.734.0
'@aws-sdk/util-user-agent-browser': 3.734.0
'@aws-sdk/util-user-agent-node': 3.734.0
'@aws-sdk/xml-builder': 3.734.0
'@smithy/config-resolver': 4.0.1
'@smithy/core': 3.1.2
'@smithy/eventstream-serde-browser': 4.0.1
'@smithy/eventstream-serde-config-resolver': 4.0.1
'@smithy/eventstream-serde-node': 4.0.1
'@smithy/fetch-http-handler': 5.0.1
'@smithy/hash-blob-browser': 4.0.1
'@smithy/hash-node': 4.0.1
'@smithy/hash-stream-node': 4.0.1
'@smithy/invalid-dependency': 4.0.1
'@smithy/md5-js': 4.0.1
'@smithy/middleware-content-length': 4.0.1
'@smithy/middleware-endpoint': 4.0.3
'@smithy/middleware-retry': 4.0.4
'@smithy/middleware-serde': 4.0.2
'@smithy/middleware-stack': 4.0.1
'@smithy/node-config-provider': 4.0.1
'@smithy/node-http-handler': 4.0.2
'@smithy/protocol-http': 5.0.1
'@smithy/smithy-client': 4.1.3
'@smithy/types': 4.1.0
'@smithy/url-parser': 4.0.1
'@smithy/util-base64': 4.0.0
'@smithy/util-body-length-browser': 4.0.0
'@smithy/util-body-length-node': 4.0.0
'@smithy/util-defaults-mode-browser': 4.0.4
'@smithy/util-defaults-mode-node': 4.0.4
'@smithy/util-endpoints': 3.0.1
'@smithy/util-middleware': 4.0.1
'@smithy/util-retry': 4.0.1
'@smithy/util-stream': 4.0.2
'@smithy/util-utf8': 4.0.0
'@smithy/util-waiter': 4.0.2
tslib: 2.7.0
transitivePeerDependencies:
- aws-crt
'@aws-sdk/client-s3@3.772.0':
dependencies:
'@aws-crypto/sha1-browser': 5.2.0
@ -13611,14 +13561,14 @@ snapshots:
'@smithy/hash-node': 4.0.1
'@smithy/invalid-dependency': 4.0.1
'@smithy/middleware-content-length': 4.0.1
'@smithy/middleware-endpoint': 4.0.3
'@smithy/middleware-endpoint': 4.0.6
'@smithy/middleware-retry': 4.0.4
'@smithy/middleware-serde': 4.0.2
'@smithy/middleware-stack': 4.0.1
'@smithy/node-config-provider': 4.0.1
'@smithy/node-http-handler': 4.0.2
'@smithy/protocol-http': 5.0.1
'@smithy/smithy-client': 4.1.3
'@smithy/smithy-client': 4.1.6
'@smithy/types': 4.1.0
'@smithy/url-parser': 4.0.1
'@smithy/util-base64': 4.0.0
@ -13883,17 +13833,6 @@ snapshots:
transitivePeerDependencies:
- aws-crt
'@aws-sdk/lib-storage@3.738.0(@aws-sdk/client-s3@3.738.0)':
dependencies:
'@aws-sdk/client-s3': 3.738.0
'@smithy/abort-controller': 4.0.1
'@smithy/middleware-endpoint': 4.0.3
'@smithy/smithy-client': 4.1.3
buffer: 5.6.0
events: 3.3.0
stream-browserify: 3.0.0
tslib: 2.7.0
'@aws-sdk/lib-storage@3.772.0(@aws-sdk/client-s3@3.772.0)':
dependencies:
'@aws-sdk/client-s3': 3.772.0
@ -13913,30 +13852,14 @@ snapshots:
'@smithy/protocol-http': 5.0.1
'@smithy/types': 4.1.0
'@smithy/util-config-provider': 4.0.0
tslib: 2.7.0
tslib: 2.8.1
'@aws-sdk/middleware-expect-continue@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
'@smithy/protocol-http': 5.0.1
'@smithy/types': 4.1.0
tslib: 2.7.0
'@aws-sdk/middleware-flexible-checksums@3.735.0':
dependencies:
'@aws-crypto/crc32': 5.2.0
'@aws-crypto/crc32c': 5.2.0
'@aws-crypto/util': 5.2.0
'@aws-sdk/core': 3.734.0
'@aws-sdk/types': 3.734.0
'@smithy/is-array-buffer': 4.0.0
'@smithy/node-config-provider': 4.0.1
'@smithy/protocol-http': 5.0.1
'@smithy/types': 4.1.0
'@smithy/util-middleware': 4.0.1
'@smithy/util-stream': 4.0.2
'@smithy/util-utf8': 4.0.0
tslib: 2.7.0
tslib: 2.8.1
'@aws-sdk/middleware-flexible-checksums@3.758.0':
dependencies:
@ -13965,7 +13888,7 @@ snapshots:
dependencies:
'@aws-sdk/types': 3.734.0
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@aws-sdk/middleware-logger@3.734.0':
dependencies:
@ -13987,23 +13910,6 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
'@aws-sdk/middleware-sdk-s3@3.734.0':
dependencies:
'@aws-sdk/core': 3.734.0
'@aws-sdk/types': 3.734.0
'@aws-sdk/util-arn-parser': 3.723.0
'@smithy/core': 3.1.2
'@smithy/node-config-provider': 4.0.1
'@smithy/protocol-http': 5.0.1
'@smithy/signature-v4': 5.0.1
'@smithy/smithy-client': 4.1.3
'@smithy/types': 4.1.0
'@smithy/util-config-provider': 4.0.0
'@smithy/util-middleware': 4.0.1
'@smithy/util-stream': 4.0.2
'@smithy/util-utf8': 4.0.0
tslib: 2.7.0
'@aws-sdk/middleware-sdk-s3@3.758.0':
dependencies:
'@aws-sdk/core': 3.758.0
@ -14025,7 +13931,7 @@ snapshots:
dependencies:
'@aws-sdk/types': 3.734.0
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@aws-sdk/middleware-user-agent@3.734.0':
dependencies:
@ -14067,14 +13973,14 @@ snapshots:
'@smithy/hash-node': 4.0.1
'@smithy/invalid-dependency': 4.0.1
'@smithy/middleware-content-length': 4.0.1
'@smithy/middleware-endpoint': 4.0.3
'@smithy/middleware-endpoint': 4.0.6
'@smithy/middleware-retry': 4.0.4
'@smithy/middleware-serde': 4.0.2
'@smithy/middleware-stack': 4.0.1
'@smithy/node-config-provider': 4.0.1
'@smithy/node-http-handler': 4.0.2
'@smithy/protocol-http': 5.0.1
'@smithy/smithy-client': 4.1.3
'@smithy/smithy-client': 4.1.6
'@smithy/types': 4.1.0
'@smithy/url-parser': 4.0.1
'@smithy/util-base64': 4.0.0
@ -14142,17 +14048,6 @@ snapshots:
'@smithy/util-middleware': 4.0.1
tslib: 2.7.0
'@aws-sdk/s3-request-presigner@3.738.0':
dependencies:
'@aws-sdk/signature-v4-multi-region': 3.734.0
'@aws-sdk/types': 3.734.0
'@aws-sdk/util-format-url': 3.734.0
'@smithy/middleware-endpoint': 4.0.3
'@smithy/protocol-http': 5.0.1
'@smithy/smithy-client': 4.1.3
'@smithy/types': 4.1.0
tslib: 2.7.0
'@aws-sdk/s3-request-presigner@3.772.0':
dependencies:
'@aws-sdk/signature-v4-multi-region': 3.758.0
@ -14164,15 +14059,6 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
'@aws-sdk/signature-v4-multi-region@3.734.0':
dependencies:
'@aws-sdk/middleware-sdk-s3': 3.734.0
'@aws-sdk/types': 3.734.0
'@smithy/protocol-http': 5.0.1
'@smithy/signature-v4': 5.0.1
'@smithy/types': 4.1.0
tslib: 2.7.0
'@aws-sdk/signature-v4-multi-region@3.758.0':
dependencies:
'@aws-sdk/middleware-sdk-s3': 3.758.0
@ -14211,7 +14097,7 @@ snapshots:
'@aws-sdk/util-arn-parser@3.723.0':
dependencies:
tslib: 2.7.0
tslib: 2.8.1
'@aws-sdk/util-endpoints@3.734.0':
dependencies:
@ -14232,7 +14118,7 @@ snapshots:
'@aws-sdk/types': 3.734.0
'@smithy/querystring-builder': 4.0.1
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@aws-sdk/util-locate-window@3.568.0':
dependencies:
@ -14264,7 +14150,7 @@ snapshots:
'@aws-sdk/xml-builder@3.734.0':
dependencies:
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@babel/code-frame@7.23.5':
dependencies:
@ -14785,11 +14671,11 @@ snapshots:
'@formatjs/icu-skeleton-parser@1.8.12':
dependencies:
'@formatjs/ecma402-abstract': 2.3.2
tslib: 2.7.0
tslib: 2.8.1
'@formatjs/intl-localematcher@0.5.10':
dependencies:
tslib: 2.7.0
tslib: 2.8.1
'@gar/promisify@1.1.3': {}
@ -22603,9 +22489,9 @@ snapshots:
'@rush-temp/s3@file:projects/s3.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))':
dependencies:
'@aws-sdk/client-s3': 3.738.0
'@aws-sdk/lib-storage': 3.738.0(@aws-sdk/client-s3@3.738.0)
'@aws-sdk/s3-request-presigner': 3.738.0
'@aws-sdk/client-s3': 3.772.0
'@aws-sdk/lib-storage': 3.772.0(@aws-sdk/client-s3@3.772.0)
'@aws-sdk/s3-request-presigner': 3.772.0
'@smithy/node-http-handler': 4.0.2
'@types/jest': 29.5.12
'@types/node': 20.11.19
@ -26033,6 +25919,7 @@ snapshots:
date-fns-tz: 2.0.0(date-fns@2.30.0)
dompurify: 3.1.6
emoji-regex: 10.3.0
emojibase: 16.0.0
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
@ -26040,7 +25927,9 @@ snapshots:
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
fast-equals: 5.2.2
hls.js: 1.5.20
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
plyr: 3.7.8
prettier: 3.2.5
prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.19)
sass: 1.71.1
@ -26205,7 +26094,6 @@ snapshots:
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
fast-equals: 5.2.2
hls.js: 1.5.20
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
prettier: 3.2.5
prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.19)
@ -26512,11 +26400,11 @@ snapshots:
'@smithy/chunked-blob-reader-native@4.0.0':
dependencies:
'@smithy/util-base64': 4.0.0
tslib: 2.7.0
tslib: 2.8.1
'@smithy/chunked-blob-reader@5.0.0':
dependencies:
tslib: 2.7.0
tslib: 2.8.1
'@smithy/config-resolver@4.0.1':
dependencies:
@ -26567,24 +26455,24 @@ snapshots:
dependencies:
'@smithy/eventstream-serde-universal': 4.0.1
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@smithy/eventstream-serde-config-resolver@4.0.1':
dependencies:
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@smithy/eventstream-serde-node@4.0.1':
dependencies:
'@smithy/eventstream-serde-universal': 4.0.1
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@smithy/eventstream-serde-universal@4.0.1':
dependencies:
'@smithy/eventstream-codec': 4.0.1
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@smithy/fetch-http-handler@5.0.1':
dependencies:
@ -26599,7 +26487,7 @@ snapshots:
'@smithy/chunked-blob-reader': 5.0.0
'@smithy/chunked-blob-reader-native': 4.0.0
'@smithy/types': 4.1.0
tslib: 2.7.0
tslib: 2.8.1
'@smithy/hash-node@4.0.1':
dependencies:
@ -26612,7 +26500,7 @@ snapshots:
dependencies:
'@smithy/types': 4.1.0
'@smithy/util-utf8': 4.0.0
tslib: 2.7.0
tslib: 2.8.1
'@smithy/invalid-dependency@4.0.1':
dependencies:
@ -26625,13 +26513,13 @@ snapshots:
'@smithy/is-array-buffer@4.0.0':
dependencies:
tslib: 2.7.0
tslib: 2.8.1
'@smithy/md5-js@4.0.1':
dependencies:
'@smithy/types': 4.1.0
'@smithy/util-utf8': 4.0.0
tslib: 2.7.0
tslib: 2.8.1
'@smithy/middleware-content-length@4.0.1':
dependencies:
@ -26861,7 +26749,7 @@ snapshots:
'@smithy/util-hex-encoding@4.0.0':
dependencies:
tslib: 2.7.0
tslib: 2.8.1
'@smithy/util-middleware@4.0.1':
dependencies:
@ -28691,7 +28579,7 @@ snapshots:
async-mutex@0.3.2:
dependencies:
tslib: 2.7.0
tslib: 2.8.1
async@3.2.5: {}
@ -29045,7 +28933,7 @@ snapshots:
camel-case@4.1.2:
dependencies:
pascal-case: 3.1.2
tslib: 2.6.2
tslib: 2.8.1
camelcase-keys@9.1.3:
dependencies:
@ -29377,6 +29265,8 @@ snapshots:
serialize-javascript: 6.0.2
webpack: 5.97.1(esbuild@0.24.2)(webpack-cli@5.1.4)
core-js@3.41.0: {}
core-util-is@1.0.2:
optional: true
@ -29542,6 +29432,8 @@ snapshots:
lodash: 4.17.21
strip-bom: 2.0.0
custom-event-polyfill@1.0.7: {}
cytoscape-cose-bilkent@4.1.0(cytoscape@3.30.4):
dependencies:
cose-base: 1.0.3
@ -30178,6 +30070,8 @@ snapshots:
emoji-regex@9.2.2: {}
emojibase@16.0.0: {}
emojis-list@3.0.0: {}
enabled@2.0.0: {}
@ -32821,6 +32715,8 @@ snapshots:
emojis-list: 3.0.0
json5: 2.2.3
loadjs@4.3.0: {}
local-pkg@0.5.1:
dependencies:
mlly: 1.7.3
@ -33688,7 +33584,7 @@ snapshots:
param-case@3.0.4:
dependencies:
dot-case: 3.0.4
tslib: 2.6.2
tslib: 2.8.1
parent-module@1.0.1:
dependencies:
@ -33899,6 +33795,14 @@ snapshots:
base64-js: 1.5.1
xmlbuilder: 15.1.1
plyr@3.7.8:
dependencies:
core-js: 3.41.0
custom-event-polyfill: 1.0.7
loadjs: 4.3.0
rangetouch: 2.0.1
url-polyfill: 1.1.13
png-chunks-extract@1.0.0:
dependencies:
crc-32: 0.3.0
@ -34275,6 +34179,8 @@ snapshots:
range-parser@1.2.1: {}
rangetouch@2.0.1: {}
raw-body@2.5.2:
dependencies:
bytes: 3.1.2
@ -35823,6 +35729,8 @@ snapshots:
querystringify: 2.2.0
requires-port: 1.0.0
url-polyfill@1.1.13: {}
url-template@2.0.8: {}
urlpattern-polyfill@10.0.0: {}

View File

@ -155,6 +155,21 @@ export function getFileUploadParams (blobId: string, blob: Blob): FileUploadPara
return { method, url, headers }
}
/**
* @public
*/
export function getBlobUrl (file: string): string {
if (file.includes('://')) {
return file
}
const fileUrl = getFileUrl(file)
const u = new URL(fileUrl)
if (u.searchParams.has('file')) {
return u.toString()
}
return fileUrl.split('/').slice(0, -1).join('/')
}
/**
* @public
*/

View File

@ -2,7 +2,7 @@ import type { Blob, Ref } from '@hcengineering/core'
import { concatLink } from '@hcengineering/core'
import { getMetadata } from '@hcengineering/platform'
import { getFileUrl, getCurrentWorkspaceId } from './file'
import { getFileUrl, getCurrentWorkspaceId, getBlobUrl } from './file'
import presentation from './plugin'
export interface PreviewConfig {
@ -11,9 +11,12 @@ export interface PreviewConfig {
}
export interface VideoMeta {
status: 'ready' | 'error' | 'inprogress' | 'queued' | 'downloading' | 'pendingupload'
thumbnail: string
hls: string
hls?: HLSMeta
}
export interface HLSMeta {
thumbnail?: string
source?: string
}
const defaultImagePreview = (): string => `/files/${getCurrentWorkspaceId()}?file=:blobId&size=:size`
@ -142,7 +145,12 @@ export async function getVideoMeta (file: string, filename?: string): Promise<Vi
try {
const response = await fetch(url, { headers: { Authorization: `Bearer ${token}` } })
if (response.ok) {
return (await response.json()) as VideoMeta
const result = (await response.json()) as VideoMeta
if (result.hls !== undefined) {
result.hls.source = getBlobUrl(result.hls.source ?? '')
result.hls.thumbnail = getBlobUrl(result.hls.thumbnail ?? '')
}
return result
}
} catch {}
}

View File

@ -96,6 +96,27 @@
}
}
:root {
.plyr__volume {
position: relative;
}
.plyr__volume input[data-plyr="volume"] {
display: none;
height: 2rem;
position: absolute;
right: -3rem;
top: -1rem;
transform-origin: left;
transform: rotate(-90deg);
}
.plyr__volume:hover input[data-plyr="volume"],
.plyr__volume input[data-plyr="volume"]:hover {
display: block;
}
--plyr-color-main: #ffffff;
--plyr-video-control-background-hover: #d3d3d3a2;
--app-height: 100%;
}

View File

@ -49,7 +49,10 @@
"date-fns": "^2.30.0",
"date-fns-tz": "^2.0.0",
"dompurify": "^3.1.6",
"@hcengineering/analytics": "^0.6.0"
"@hcengineering/analytics": "^0.6.0",
"emojibase": "^16.0.0",
"hls.js": "^1.5.20",
"plyr": "^3.7.8"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {

View File

@ -0,0 +1,170 @@
<!--
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import HLS, { LoadPolicy } from 'hls.js'
import { onDestroy, onMount } from 'svelte'
import Plyr from 'plyr'
export let src: string
export let hlsSrc: string
export let hlsThumbnail = ''
export let name: string | undefined = undefined
export let preload = true
let video: HTMLVideoElement | null = null
let hls: HLS | null = null
let player: Plyr | null = null
const options: Plyr.Options = {}
function initialize (src: string): void {
if (video === null) {
return
}
if (!HLS.isSupported()) {
video.src = src
player = new Plyr(video, options)
return
}
const originalUrl = new URL(src)
hls?.destroy()
player?.destroy()
const loadPolicy: LoadPolicy = {
default: {
maxTimeToFirstByteMs: Infinity,
maxLoadTimeMs: 1000 * 60,
timeoutRetry: {
maxNumRetry: 20,
retryDelayMs: 100,
maxRetryDelayMs: 250
},
errorRetry: {
maxNumRetry: 20,
retryDelayMs: 100,
maxRetryDelayMs: 250,
shouldRetry: (retryConfig, retryCount) => retryCount < (retryConfig?.maxNumRetry ?? 10)
}
}
}
hls = new HLS({
manifestLoadPolicy: loadPolicy,
playlistLoadPolicy: loadPolicy,
autoStartLoad: preload,
xhrSetup: (xhr, url) => {
const urlObj = new URL(url)
if (urlObj.searchParams.size > 1) {
return
}
const workspace = originalUrl.searchParams.get('workspace')
if (workspace == null) {
return
}
xhr.withCredentials = true
const fileName = urlObj.href.substring(urlObj.href.lastIndexOf('/') + 1)
urlObj.searchParams.append('file', fileName)
urlObj.searchParams.append('workspace', workspace)
xhr.open('GET', urlObj.toString(), true)
}
})
hls.loadSource(hlsSrc)
hls.attachMedia(video)
video.poster = hlsThumbnail
if (!preload) {
video.onplay = () => {
if (video === null) {
return
}
video.onplay = null
hls?.startLoad()
}
}
hls.on(HLS.Events.MANIFEST_PARSED, () => {
if (hls === null) {
return
}
const availableQualities = hls?.levels.map((l) => l.height)
availableQualities.unshift(0)
options.quality = {
default: 0,
options: availableQualities,
forced: true,
onChange: (e) => {
updateQuality(e)
}
}
options.captions = {
active: false
}
options.i18n = {
qualityLabel: {
0: 'Auto'
}
}
if (video === null) {
return
}
player = new Plyr(video, options)
})
}
$: initialize(src)
onMount(() => {
initialize(src)
})
onDestroy(() => {
hls?.destroy()
player?.destroy()
})
function updateQuality (newQuality: number): void {
if (hls === null) {
return
}
if (newQuality === 0) {
hls.currentLevel = -1
} else {
hls.levels.forEach((level, levelIndex) => {
if (level.height === newQuality && hls !== null) {
hls.currentLevel = levelIndex
}
})
}
}
</script>
<!-- svelte-ignore a11y-media-has-caption -->
<video bind:this={video} width="100%" height="100%" preload={preload ? 'auto' : 'none'} controls />
<style lang="scss">
video {
border-radius: inherit;
object-fit: contain;
}
video::-webkit-media-controls {
visibility: hidden;
}
video::-webkit-media-controls-enclosure {
visibility: visible;
}
@import 'plyr/dist/plyr.css';
</style>

View File

@ -0,0 +1,40 @@
<!--
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
export let src: string
export let name: string = ''
export let preload = true
let video: HTMLVideoElement
</script>
<video bind:this={video} {src} width="100%" height="100%" preload={preload ? 'auto' : 'none'} controls>
<track kind="captions" label={name} />
</video>
<style lang="scss">
video {
border-radius: inherit;
object-fit: contain;
}
video::-webkit-media-controls {
visibility: hidden;
}
video::-webkit-media-controls-enclosure {
visibility: visible;
}
</style>

View File

@ -286,6 +286,9 @@ export { default as NestedDropdown } from './components/NestedDropdown.svelte'
export { default as Dock } from './components/Dock.svelte'
export { default as Video } from './components/Video.svelte'
export { default as HlsVideo } from './components/HlsVideo.svelte'
export * from './types'
export * from './location'
export * from './utils'

View File

@ -36,7 +36,7 @@
export let withShowMore: boolean = false
export let attachmentImageSize: 'x-large' | undefined = undefined
export let type: ActivityMessageViewType = 'default'
export let videoPreload = true
export let videoPreload = false
export let hideLink = false
export let compact = false
export let readonly = false

View File

@ -26,7 +26,7 @@
export let value: Doc & { attachments?: number }
export let attachments: Attachment[] | undefined = undefined
export let imageSize: AttachmentImageSize = 'auto'
export let videoPreload = true
export let videoPreload = false
const query = createQuery()

View File

@ -23,7 +23,7 @@
export let attachments: WithLookup<Attachment>[]
export let savedAttachmentsIds: Ref<Attachment>[] = []
export let imageSize: AttachmentImageSize = 'auto'
export let videoPreload = true
export let videoPreload = false
let otherAttachments: WithLookup<Attachment>[]
let linkPreviewAttachments: WithLookup<Attachment>[]

View File

@ -22,7 +22,7 @@
export let attachments: WithLookup<Attachment>[] = []
export let savedAttachmentsIds: Ref<Attachment>[] = []
export let imageSize: AttachmentImageSize | undefined = undefined
export let videoPreload = true
export let videoPreload = false
</script>
{#if attachments.length}

View File

@ -31,7 +31,7 @@
export let listProvider: ListSelectionProvider | undefined = undefined
export let imageSize: AttachmentImageSize = 'auto'
export let removable: boolean = false
export let videoPreload = true
export let videoPreload = false
const dispatch = createEventDispatcher()

View File

@ -16,17 +16,19 @@
import type { Attachment } from '@hcengineering/attachment'
import type { BlobType, WithLookup } from '@hcengineering/core'
import { getFileUrl } from '@hcengineering/presentation'
import AttachmentPresenter from './AttachmentPresenter.svelte'
import { getFileUrl, getVideoMeta } from '@hcengineering/presentation'
import { HlsVideo, Video } from '@hcengineering/ui'
export let value: WithLookup<Attachment> | BlobType
export let preload = true
export let preload = false
const maxSizeRem = 20
const baseSizeRem = 12
const minSizeRem = 4
const maxSizeRem = 25
const baseSizeRem = 20
const minSizeRem = 10
$: dimensions = getDimensions(value)
$: name = value.name
$: file = value.file
function getDimensions (value: Attachment | BlobType): { width: number, height: number } {
const fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize)
@ -55,22 +57,28 @@
return { width, height }
}
function toStyle (size: 'auto' | number): string {
return size === 'auto' ? 'auto' : `${size}px`
}
</script>
<video controls width={dimensions.width} height={dimensions.height} preload={preload ? 'auto' : 'none'}>
<source src={getFileUrl(value.file, value.name)} />
<track kind="captions" label={value.name} />
<div class="container">
<AttachmentPresenter {value} />
</div>
</video>
<div class="container" style="width:{toStyle(dimensions.width)}; height:{toStyle(dimensions.height)}">
{#await getVideoMeta(file, name) then meta}
{@const src = getFileUrl(file, name)}
{#if meta?.hls?.source !== undefined}
<HlsVideo {src} {preload} hlsSrc={meta.hls.source} hlsThumbnail={meta.hls.thumbnail} {name} />
{:else}
<Video {src} {name} preload />
{/if}
{/await}
</div>
<style lang="scss">
video {
max-width: 20rem;
max-height: 20rem;
min-width: 4rem;
min-height: 4rem;
.container {
min-width: 10rem;
min-height: 10rem;
border-radius: 0.75rem;
}
</style>

View File

@ -49,7 +49,7 @@
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
export let withShowMore: boolean = true
export let attachmentImageSize: AttachmentImageSize = 'auto'
export let videoPreload = true
export let videoPreload = false
export let hideLink = false
export let compact = false
export let readonly = false

View File

@ -38,7 +38,7 @@
export let withShowMore: boolean = true
export let hoverStyles: 'borderedHover' | 'filledHover' = 'borderedHover'
export let attachmentImageSize: AttachmentImageSize = 'x-large'
export let videoPreload = true
export let videoPreload = false
export let readonly = false
export let onClick: (() => void) | undefined = undefined

View File

@ -58,7 +58,6 @@
"@hcengineering/text-editor-resources": "^0.6.0",
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/query": "^0.6.12",
"fast-equals": "^5.2.2",
"hls.js": "^1.5.20"
"fast-equals": "^5.2.2"
}
}

View File

@ -13,54 +13,35 @@
// limitations under the License.
-->
<script lang="ts">
import { type Blob, type BlobMetadata, type Ref } from '@hcengineering/core'
import { getFileUrl, getVideoMeta } from '@hcengineering/presentation'
import { onDestroy } from 'svelte'
import HLS from 'hls.js'
import { type Blob, type Ref } from '@hcengineering/core'
import { getFileUrl, getVideoMeta, type BlobMetadata } from '@hcengineering/presentation'
import { HlsVideo, Video } from '@hcengineering/ui'
export let value: Ref<Blob>
export let name: string
export let metadata: BlobMetadata | undefined
export let fit: boolean = false
let video: HTMLVideoElement
let hls: HLS
async function fetchVideoMeta (value: Ref<Blob>, name: string): Promise<void> {
const src = getFileUrl(value, name)
const meta = await getVideoMeta(value, name)
if (meta != null && meta.status === 'ready' && HLS.isSupported()) {
hls?.destroy()
hls = new HLS()
hls.loadSource(meta.hls)
hls.attachMedia(video)
video.poster = meta.thumbnail
} else {
video.src = src
}
}
onDestroy(() => {
hls?.destroy()
})
$: aspectRatio =
metadata?.originalWidth && metadata?.originalHeight
? `${metadata.originalWidth} / ${metadata.originalHeight}`
: '16 / 9'
$: maxWidth = metadata?.originalWidth ? `min(${metadata.originalWidth}px, 100%)` : undefined
$: maxHeight = metadata?.originalHeight ? `min(${metadata.originalHeight}px, 80vh)` : undefined
$: void fetchVideoMeta(value, name)
</script>
<video
bind:this={video}
width="100%"
<div
style:aspect-ratio={aspectRatio}
style:max-width={fit ? '100%' : maxWidth}
style:max-height={fit ? '100%' : maxHeight}
controls
preload={'auto'}
>
<track kind="captions" label={name} />
</video>
{#await getVideoMeta(value, name) then meta}
{#if meta?.hls?.source !== undefined}
{@const src = getFileUrl(value, name)}
<HlsVideo {src} {name} hlsSrc={meta.hls.source} hlsThumbnail={meta.hls.thumbnail} preload={false} />
{:else}
{@const src = getFileUrl(value, name)}
<Video {src} {name} />
{/if}
{/await}
</div>

View File

@ -26,6 +26,7 @@ export interface Config {
Port: number
Secret: string
AccountsUrl: string
StreamUrl?: string
DbUrl: string
Buckets: BucketConfig[]
}
@ -75,6 +76,7 @@ const config: Config = (() => {
Secret: process.env.SECRET,
AccountsUrl: process.env.ACCOUNTS_URL,
DbUrl: process.env.DB_URL,
StreamUrl: process.env.STREAM_URL,
Buckets: parseBucketsConfig(process.env.BUCKETS)
}

View File

@ -19,7 +19,7 @@ import { Readable } from 'stream'
import { type BlobDB } from './db'
import { digestToUUID, stringToUUID } from './encodings'
import { type BlobHead, type BlobBody, type BlobList, type BlobStorage, type Datalake, type Location } from './types'
import { requestHLS } from '../handlers/video'
import { type S3Bucket } from '../s3'
export class DatalakeImpl implements Datalake {
@ -144,6 +144,9 @@ export class DatalakeImpl implements Datalake {
}
await bucket.put(ctx, filename, body, putOptions)
await this.db.createBlobData(ctx, { workspace, name, hash, location, filename, size, type: contentType })
if (contentType.startsWith('video/')) {
void requestHLS(ctx, workspace, name)
}
return { name, size, contentType, lastModified, etag: hash }
}
}
@ -168,6 +171,10 @@ export class DatalakeImpl implements Datalake {
await this.db.createBlobData(ctx, { workspace, name, hash, location, filename, size, type: contentType })
}
if (contentType.startsWith('video/')) {
void requestHLS(ctx, workspace, name)
}
return { name, size, contentType, lastModified, etag: hash }
}

View File

@ -227,6 +227,8 @@ export async function handleUploadFormData (
lastModified: Date.now()
})
ctx.info('uploaded', { workspace, name, etag: metadata.etag, type: contentType })
return { key, metadata }
} catch (err: any) {
const error = err instanceof Error ? err.message : String(err)

View File

@ -0,0 +1,57 @@
//
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { type MeasureContext, getWorkspaceId, systemAccountEmail } from '@hcengineering/core'
import { generateToken } from '@hcengineering/server-token'
import config from '../config'
interface StreamRequest {
source: string
format: string
workspace: string
metadata?: Record<string, string>
}
export async function requestHLS (ctx: MeasureContext, workspace: string, name: string): Promise<void> {
try {
ctx.info('request for hls', { workspace, name })
await postTranscodingTask(ctx, workspace, name)
} catch (err) {
ctx.error('can not schedule a task', { err })
}
}
async function postTranscodingTask (ctx: MeasureContext, workspace: string, name: string): Promise<void> {
if (config.StreamUrl === undefined) {
return
}
const streamReq: StreamRequest = { format: 'hls', source: name, workspace }
const token = generateToken(systemAccountEmail, getWorkspaceId(''), { iss: 'datalake', aud: 'stream' })
const request = new Request(config.StreamUrl, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(streamReq)
})
const resp = await fetch(request)
if (!resp.ok) {
ctx.error(resp.statusText)
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig"
}

View File

@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}