diff --git a/lib/Controller/VideoController.php b/lib/Controller/VideoController.php index 781dbd6..c8f4c81 100644 --- a/lib/Controller/VideoController.php +++ b/lib/Controller/VideoController.php @@ -120,7 +120,27 @@ class VideoController extends OCSController { // Get file info $fileSize = $file->getSize(); - $mimeType = $file->getMimeType(); + + // Override MIME type based on file extension for better browser compatibility + $fileName = $file->getName(); + $extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); + $mimeTypeMap = [ + 'mp4' => 'video/mp4', + 'webm' => 'video/webm', + 'ogg' => 'video/ogg', + 'ogv' => 'video/ogg', + 'mkv' => 'video/webm', // Try webm MIME type as webm is a subset of MKV + 'avi' => 'video/x-msvideo', + 'mov' => 'video/quicktime', + ]; + $mimeType = $mimeTypeMap[$extension] ?? $file->getMimeType(); + + $this->logger->info('Streaming video file', [ + 'fileName' => $fileName, + 'extension' => $extension, + 'mimeType' => $mimeType, + 'fileSize' => $fileSize, + ]); // Handle range requests for video seeking $rangeHeader = $this->request->getHeader('range'); diff --git a/package.json b/package.json index 4642681..7118fea 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "chart.js": "^4.5.0", "date-fns": "^4.1.0", "linkifyjs": "^4.3.2", + "video.js": "^8.23.4", "vue": "^3.5.22", "vue-material-design-icons": "^5.3.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 104f0fc..e1f3efb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: linkifyjs: specifier: ^4.3.2 version: 4.3.2 + video.js: + specifier: ^8.23.4 + version: 8.23.4 vue: specifier: ^3.5.22 version: 3.5.22(typescript@5.9.3) @@ -1059,6 +1062,19 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@videojs/http-streaming@3.17.2': + resolution: {integrity: sha512-VBQ3W4wnKnVKb/limLdtSD2rAd5cmHN70xoMf4OmuDd0t2kfJX04G+sfw6u2j8oOm2BXYM9E1f4acHruqKnM1g==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + video.js: ^8.19.0 + + '@videojs/vhs-utils@4.1.1': + resolution: {integrity: sha512-5iLX6sR2ownbv4Mtejw6Ax+naosGvoT9kY+gcuHzANyUZZ+4NpeNdKMUhb6ag0acYej1Y7cmr/F2+4PrggMiVA==} + engines: {node: '>=8', npm: '>=5'} + + '@videojs/xhr@2.7.0': + resolution: {integrity: sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ==} + '@vitejs/plugin-vue@5.2.4': resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1190,6 +1206,10 @@ packages: peerDependencies: vue: ^3.5.0 + '@xmldom/xmldom@0.8.11': + resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} + engines: {node: '>=10.0.0'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1200,6 +1220,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + aes-decrypter@4.0.2: + resolution: {integrity: sha512-lc+/9s6iJvuaRe5qDlMTpCFjnwpkeOXp8qP3oiZ5jsj1MRg+SBVUmmICrhxHvc8OELSmc+fEyyxAuppY6hrWzw==} + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -1694,6 +1717,9 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dom-walk@0.1.2: + resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} + domain-browser@4.22.0: resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} engines: {node: '>=10'} @@ -2150,6 +2176,9 @@ packages: resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} engines: {node: '>=6'} + global@4.4.0: + resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} + globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} @@ -2379,6 +2408,9 @@ packages: resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} engines: {node: '>=18'} + is-function@1.0.2: + resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -2592,6 +2624,9 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} + m3u8-parser@7.2.0: + resolution: {integrity: sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==} + magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} @@ -2738,6 +2773,9 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} + min-document@2.19.0: + resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} + minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} @@ -2764,12 +2802,21 @@ packages: moment@2.30.1: resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + mpd-parser@1.3.1: + resolution: {integrity: sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw==} + hasBin: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + mux.js@7.1.0: + resolution: {integrity: sha512-NTxawK/BBELJrYsZThEulyUMDVlLizKdxyAsMuzoCD1eFj97BVaA8D/CvKsKu6FOLYkFojN5CbM9h++ZTZtknA==} + engines: {node: '>=8', npm: '>=5'} + hasBin: true + nano-spawn@1.0.3: resolution: {integrity: sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA==} engines: {node: '>=20.17'} @@ -2936,6 +2983,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + pkcs7@1.0.4: + resolution: {integrity: sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==} + hasBin: true + pkg-dir@5.0.0: resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} engines: {node: '>=10'} @@ -3758,6 +3809,21 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + video.js@8.23.4: + resolution: {integrity: sha512-qI0VTlYmKzEqRsz1Nppdfcaww4RSxZAq77z2oNSl3cNg2h6do5C8Ffl0KqWQ1OpD8desWXsCrde7tKJ9gGTEyQ==} + + videojs-contrib-quality-levels@4.1.0: + resolution: {integrity: sha512-TfrXJJg1Bv4t6TOCMEVMwF/CoS8iENYsWNKip8zfhB5kTcegiFYezEA0eHAJPU64ZC8NQbxQgOwAsYU8VXbOWA==} + engines: {node: '>=16', npm: '>=8'} + peerDependencies: + video.js: ^8 + + videojs-font@4.2.0: + resolution: {integrity: sha512-YPq+wiKoGy2/M7ccjmlvwi58z2xsykkkfNMyIg4xb7EZQQNwB71hcSsB3o75CqQV7/y5lXkXhI/rsGAS7jfEmQ==} + + videojs-vtt.js@0.15.5: + resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==} + vite-plugin-css-injected-by-js@3.5.2: resolution: {integrity: sha512-2MpU/Y+SCZyWUB6ua3HbJCrgnF0KACAsmzOQt1UvRVJCGF6S8xdA3ZUhWcWdM9ivG4I5az8PnQmwwrkC2CAQrQ==} peerDependencies: @@ -4969,6 +5035,28 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@videojs/http-streaming@3.17.2(video.js@8.23.4)': + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/vhs-utils': 4.1.1 + aes-decrypter: 4.0.2 + global: 4.4.0 + m3u8-parser: 7.2.0 + mpd-parser: 1.3.1 + mux.js: 7.1.0 + video.js: 8.23.4 + + '@videojs/vhs-utils@4.1.1': + dependencies: + '@babel/runtime': 7.28.4 + global: 4.4.0 + + '@videojs/xhr@2.7.0': + dependencies: + '@babel/runtime': 7.28.4 + global: 4.4.0 + is-function: 1.0.2 + '@vitejs/plugin-vue@5.2.4(vite@6.3.6(@types/node@20.17.10)(sass-embedded@1.93.2)(sass@1.93.2)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: vite: 6.3.6(@types/node@20.17.10)(sass-embedded@1.93.2)(sass@1.93.2)(yaml@2.8.1) @@ -5145,12 +5233,21 @@ snapshots: dependencies: vue: 3.5.22(typescript@5.9.3) + '@xmldom/xmldom@0.8.11': {} + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} + aes-decrypter@4.0.2: + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/vhs-utils': 4.1.1 + global: 4.4.0 + pkcs7: 1.0.4 + ajv-draft-04@1.0.0(ajv@8.13.0): optionalDependencies: ajv: 8.13.0 @@ -5682,6 +5779,8 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 + dom-walk@0.1.2: {} + domain-browser@4.22.0: {} domelementtype@2.3.0: {} @@ -6279,6 +6378,11 @@ snapshots: kind-of: 6.0.3 which: 1.3.1 + global@4.4.0: + dependencies: + min-document: 2.19.0 + process: 0.11.10 + globals@13.24.0: dependencies: type-fest: 0.20.2 @@ -6517,6 +6621,8 @@ snapshots: dependencies: get-east-asian-width: 1.4.0 + is-function@1.0.2: {} + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -6720,6 +6826,12 @@ snapshots: dependencies: yallist: 4.0.0 + m3u8-parser@7.2.0: + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/vhs-utils': 4.1.1 + global: 4.4.0 + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -7005,6 +7117,10 @@ snapshots: mimic-function@5.0.1: {} + min-document@2.19.0: + dependencies: + dom-walk: 0.1.2 + minimalistic-assert@1.0.1: {} minimalistic-crypto-utils@1.0.1: {} @@ -7032,10 +7148,22 @@ snapshots: moment@2.30.1: {} + mpd-parser@1.3.1: + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/vhs-utils': 4.1.1 + '@xmldom/xmldom': 0.8.11 + global: 4.4.0 + ms@2.1.3: {} muggle-string@0.4.1: {} + mux.js@7.1.0: + dependencies: + '@babel/runtime': 7.28.4 + global: 4.4.0 + nano-spawn@1.0.3: {} nanoid@3.3.11: {} @@ -7231,6 +7359,10 @@ snapshots: pidtree@0.6.0: {} + pkcs7@1.0.4: + dependencies: + '@babel/runtime': 7.28.4 + pkg-dir@5.0.0: dependencies: find-up: 5.0.0 @@ -8210,6 +8342,32 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + video.js@8.23.4: + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/http-streaming': 3.17.2(video.js@8.23.4) + '@videojs/vhs-utils': 4.1.1 + '@videojs/xhr': 2.7.0 + aes-decrypter: 4.0.2 + global: 4.4.0 + m3u8-parser: 7.2.0 + mpd-parser: 1.3.1 + mux.js: 7.1.0 + videojs-contrib-quality-levels: 4.1.0(video.js@8.23.4) + videojs-font: 4.2.0 + videojs-vtt.js: 0.15.5 + + videojs-contrib-quality-levels@4.1.0(video.js@8.23.4): + dependencies: + global: 4.4.0 + video.js: 8.23.4 + + videojs-font@4.2.0: {} + + videojs-vtt.js@0.15.5: + dependencies: + global: 4.4.0 + vite-plugin-css-injected-by-js@3.5.2(vite@6.3.6(@types/node@20.17.10)(sass-embedded@1.93.2)(sass@1.93.2)(yaml@2.8.1)): dependencies: vite: 6.3.6(@types/node@20.17.10)(sass-embedded@1.93.2)(sass@1.93.2)(yaml@2.8.1) diff --git a/src/views/VideoView.vue b/src/views/VideoView.vue index c65d4d6..378c253 100644 --- a/src/views/VideoView.vue +++ b/src/views/VideoView.vue @@ -5,19 +5,30 @@