diff --git a/components/previews/AudioPreview.tsx b/components/previews/AudioPreview.tsx index 9757c4a..65bf3f6 100644 --- a/components/previews/AudioPreview.tsx +++ b/components/previews/AudioPreview.tsx @@ -1,11 +1,12 @@ import type { OdFileObject } from '../../types' import { FC, useState } from 'react' -import ReactPlayer from 'react-player' +import ReactAudioPlayer from 'react-audio-player' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import DownloadButtonGroup from '../DownloadBtnGtoup' import { DownloadBtnContainer, PreviewContainer } from './Containers' +import { LoadingIcon } from '../Loading' enum PlayerState { Loading, @@ -23,21 +24,7 @@ const AudioPreview: FC<{ file: OdFileObject }> = ({ file }) => {
{playerStatus === PlayerState.Loading ? ( -
- - - - -
+ ) : ( = ({ file }) => { timeStyle: 'short', })}
- { - setPlayerStatus(PlayerState.Ready) - }} - onPlay={() => { - setPlayerStatus(PlayerState.Playing) - }} - onPause={() => { - setPlayerStatus(PlayerState.Paused) - }} + onCanPlay={() => setPlayerStatus(PlayerState.Ready)} + onPlay={() => setPlayerStatus(PlayerState.Playing)} + onPause={() => setPlayerStatus(PlayerState.Paused)} onError={() => setPlayerStatus(PlayerState.Paused)} onEnded={() => setPlayerStatus(PlayerState.Paused)} /> diff --git a/components/previews/VideoPreview.tsx b/components/previews/VideoPreview.tsx index fe0e6af..2c30b09 100644 --- a/components/previews/VideoPreview.tsx +++ b/components/previews/VideoPreview.tsx @@ -1,7 +1,7 @@ import type { OdFileObject } from '../../types' -import ReactPlayer from 'react-player' import { useRouter } from 'next/router' import { useClipboard } from 'use-clipboard-copy' +import DPlayer from 'react-dplayer' import toast from 'react-hot-toast' import { getBaseUrl } from '../../utils/getBaseUrl' @@ -12,16 +12,19 @@ const VideoPreview: React.FC<{ file: OdFileObject }> = ({ file }) => { const { asPath } = useRouter() const clipboard = useClipboard() + // OneDrive generates thumbnails for its video files, we pick the thumbnail with the highest resolution + const thumbnail = file.thumbnails && file.thumbnails.length > 0 ? file.thumbnails[0].large.url : '' + return ( <> - diff --git a/package.json b/package.json index 0633330..19546ae 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ "axios": "^0.25.0", "cors": "^2.8.5", "crypto-js": "^4.1.1", - "csstype": "^3.0.10", "emoji-regex": "^10.0.0", "ioredis": "^4.28.2", "jszip": "^3.7.1", @@ -29,12 +28,13 @@ "prismjs": "^1.23.0", "react": "^17.0.2", "react-async-hook": "^4.0.0", + "react-audio-player": "^0.17.0", "react-copy-to-clipboard": "^5.0.3", "react-dom": "^17.0.2", + "react-dplayer": "^0.4.2", "react-hot-toast": "^2.0.0", "react-hotkeys-hook": "^3.4.4", "react-markdown": "^8.0.0", - "react-player": "^2.9.0", "react-reader": "^0.20.4", "react-viewer": "^3.2.2", "rehype-katex": "^6.0.2", diff --git a/pages/api/index.ts b/pages/api/index.ts index e59aab8..5addae6 100644 --- a/pages/api/index.ts +++ b/pages/api/index.ts @@ -210,6 +210,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) headers: { Authorization: `Bearer ${accessToken}` }, params: { select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file,video,image', + $expand: 'thumbnails', }, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c058c63..c678e7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,7 +21,6 @@ specifiers: axios: ^0.25.0 cors: ^2.8.5 crypto-js: ^4.1.1 - csstype: ^3.0.10 emoji-regex: ^10.0.0 eslint: 8.8.0 eslint-config-next: 12.0.10 @@ -34,12 +33,13 @@ specifiers: prismjs: ^1.23.0 react: ^17.0.2 react-async-hook: ^4.0.0 + react-audio-player: ^0.17.0 react-copy-to-clipboard: ^5.0.3 react-dom: ^17.0.2 + react-dplayer: ^0.4.2 react-hot-toast: ^2.0.0 react-hotkeys-hook: ^3.4.4 react-markdown: ^8.0.0 - react-player: ^2.9.0 react-reader: ^0.20.4 react-viewer: ^3.2.2 rehype-katex: ^6.0.2 @@ -63,7 +63,6 @@ dependencies: axios: 0.25.0 cors: 2.8.5 crypto-js: 4.1.1 - csstype: 3.0.10 emoji-regex: 10.0.0 ioredis: 4.28.3 jszip: 3.7.1 @@ -73,12 +72,13 @@ dependencies: prismjs: 1.26.0 react: 17.0.2 react-async-hook: 4.0.0_react@17.0.2 + react-audio-player: 0.17.0_react-dom@17.0.2+react@17.0.2 react-copy-to-clipboard: 5.0.4_react@17.0.2 react-dom: 17.0.2_react@17.0.2 - react-hot-toast: 2.2.0_cf9e547307deef37c34d2702bfa94a69 + react-dplayer: 0.4.2_react@17.0.2 + react-hot-toast: 2.2.0_react-dom@17.0.2+react@17.0.2 react-hotkeys-hook: 3.4.4_react-dom@17.0.2+react@17.0.2 react-markdown: 8.0.0_b08e3c15324cbe90a6ff8fcd416c932c - react-player: 2.9.0_react@17.0.2 react-reader: 0.20.5_react@17.0.2 react-viewer: 3.2.2 rehype-katex: 6.0.2 @@ -388,6 +388,10 @@ packages: '@types/ms': 0.7.31 dev: false + /@types/dplayer/1.25.2: + resolution: {integrity: sha512-bkTVZkK3Vi7N7eX2FUBnqKhCjTaeRLkhvY8H6zolatbSTtjPPdxyUzhE3C29sIBYRRq1kQHSduFgCHKg5VF3Jw==} + dev: false + /@types/hast/2.3.4: resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} dependencies: @@ -725,6 +729,13 @@ packages: engines: {node: '>=4'} dev: true + /axios/0.19.2: + resolution: {integrity: sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==} + deprecated: Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410 + dependencies: + follow-redirects: 1.5.10 + dev: false + /axios/0.25.0: resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==} dependencies: @@ -745,6 +756,10 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /balloon-css/1.2.0: + resolution: {integrity: sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A==} + dev: false + /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -844,6 +859,11 @@ packages: resolution: {integrity: sha512-vooFaGFL6ulEP1liiaWFBmmfuPm3cY3y7T9eB83ZTnYc/oFeAKsq3NcDrOkBC8XaauEE8zHQwI7k0+JSYiVQSQ==} dev: false + /clsx/1.1.1: + resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==} + engines: {node: '>=6'} + dev: false + /cluster-key-slot/1.1.0: resolution: {integrity: sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==} engines: {node: '>=0.10.0'} @@ -943,6 +963,7 @@ packages: /csstype/3.0.10: resolution: {integrity: sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==} + dev: true /d/1.0.1: resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} @@ -965,6 +986,12 @@ packages: ms: 2.0.0 dev: true + /debug/3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + dependencies: + ms: 2.0.0 + dev: false + /debug/3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} dependencies: @@ -992,11 +1019,6 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /deepmerge/4.2.2: - resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} - engines: {node: '>=0.10.0'} - dev: false - /define-properties/1.1.3: resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==} engines: {node: '>= 0.4'} @@ -1062,6 +1084,14 @@ packages: esutils: 2.0.3 dev: true + /dplayer/1.26.0: + resolution: {integrity: sha512-uOE0w/WdlX7N9d0ppIEcAYrcnUjY52TMX+MBL4lj9Mj+JMljVuaEc5w88HkZp5Q11VqvN/jxnM8ktx2Dr7/MgA==} + dependencies: + axios: 0.19.2 + balloon-css: 1.2.0 + promise-polyfill: 8.1.3 + dev: false + /electron-to-chromium/1.4.61: resolution: {integrity: sha512-kpzCOOFlx63C9qKRyIDEsKIUgzoe98ump7T4gU+/OLzj8gYkkWf2SIyBjhTSE0keAjMAp3i7C262YtkQOMYrGw==} dev: true @@ -1500,6 +1530,13 @@ packages: optional: true dev: false + /follow-redirects/1.5.10: + resolution: {integrity: sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==} + engines: {node: '>=4.0'} + dependencies: + debug: 3.1.0 + dev: false + /fraction.js/4.1.2: resolution: {integrity: sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==} dev: true @@ -1595,12 +1632,10 @@ packages: slash: 3.0.0 dev: true - /goober/2.1.7_csstype@3.0.10: + /goober/2.1.7: resolution: {integrity: sha512-aCR8u3A/tTgSrZAHfJObhYC0xgdKoYm4GvE/UFmxmzgvj3TSF+3oFYWtmJ459WBewjOIoEsoOG81sDs1rn+W5w==} peerDependencies: csstype: ^2.6.2 - dependencies: - csstype: 3.0.10 dev: false /has-bigints/1.0.1: @@ -2026,10 +2061,6 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true - /load-script/1.0.0: - resolution: {integrity: sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ=} - dev: false - /localforage/1.10.0: resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} dependencies: @@ -2215,10 +2246,6 @@ packages: resolution: {integrity: sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=} dev: false - /memoize-one/5.2.1: - resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} - dev: false - /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2509,7 +2536,6 @@ packages: /ms/2.0.0: resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} - dev: true /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -2665,6 +2691,10 @@ packages: es-abstract: 1.19.1 dev: true + /omit.js/2.0.2: + resolution: {integrity: sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==} + dev: false + /once/1.4.0: resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} dependencies: @@ -2863,6 +2893,10 @@ packages: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: false + /promise-polyfill/8.1.3: + resolution: {integrity: sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==} + dev: false + /prop-types/15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: @@ -2897,6 +2931,17 @@ packages: react: 17.0.2 dev: false + /react-audio-player/0.17.0_react-dom@17.0.2+react@17.0.2: + resolution: {integrity: sha512-aCZgusPxA9HK7rLZcTdhTbBH9l6do9vn3NorgoDZRxRxJlOy9uZWzPaKjd7QdcuP2vXpxGA/61JMnnOEY7NXeA==} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + prop-types: 15.8.1 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + dev: false + /react-copy-to-clipboard/5.0.4_react@17.0.2: resolution: {integrity: sha512-IeVAiNVKjSPeGax/Gmkqfa/+PuMTBhutEvFUaMQLwE2tS0EXrAdgOpWDX26bWTXF3HrioorR7lr08NqeYUWQCQ==} peerDependencies: @@ -2918,18 +2963,26 @@ packages: scheduler: 0.20.2 dev: false - /react-fast-compare/3.2.0: - resolution: {integrity: sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==} + /react-dplayer/0.4.2_react@17.0.2: + resolution: {integrity: sha512-UnI6KaAsnGtP6MSz79lGCAEZko+j0DcKgwfNATtQJL/T/zRs1t3ZJDT/ymbCquuU1e3fADn0Cww1gpNJ3K2TiQ==} + peerDependencies: + react: ^16.x + dependencies: + '@types/dplayer': 1.25.2 + clsx: 1.1.1 + dplayer: 1.26.0 + omit.js: 2.0.2 + react: 17.0.2 dev: false - /react-hot-toast/2.2.0_cf9e547307deef37c34d2702bfa94a69: + /react-hot-toast/2.2.0_react-dom@17.0.2+react@17.0.2: resolution: {integrity: sha512-248rXw13uhf/6TNDVzagX+y7R8J183rp7MwUMNkcrBRyHj/jWOggfXTGlM8zAOuh701WyVW+eUaWG2LeSufX9g==} engines: {node: '>=10'} peerDependencies: react: '>=16' react-dom: '>=16' dependencies: - goober: 2.1.7_csstype@3.0.10 + goober: 2.1.7 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 transitivePeerDependencies: @@ -2980,19 +3033,6 @@ packages: - supports-color dev: false - /react-player/2.9.0_react@17.0.2: - resolution: {integrity: sha512-jNUkTfMmUhwPPAktAdIqiBcVUKsFKrVGH6Ocutj6535CNfM91yrvWxHg6fvIX8Y/fjYUPoejddwh7qboNV9vGA==} - peerDependencies: - react: '>=16.6.0' - dependencies: - deepmerge: 4.2.2 - load-script: 1.0.0 - memoize-one: 5.2.1 - prop-types: 15.8.1 - react: 17.0.2 - react-fast-compare: 3.2.0 - dev: false - /react-reader/0.20.5_react@17.0.2: resolution: {integrity: sha512-mg49s+RTm1qDLEMgvQeqh1aMm2T6wLqwevkxcIlPx1/dAP/Q0q9HIXl1N/bAifZQTQIoJt8UMAAyXp8eBzmCag==} peerDependencies: diff --git a/types/index.d.ts b/types/index.d.ts index 91eac21..1872f2f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,9 +1,9 @@ // API response object for /api?path=, this may return either a file or a folder. // Pagination is also declared here with the 'next' parameter. -export declare type OdAPIResponse = { file?: OdFileObject; folder?: OdFolderObject; next?: string } +export type OdAPIResponse = { file?: OdFileObject; folder?: OdFolderObject; next?: string } // A folder object returned from the OneDrive API. This contains the parameter 'value', which is an array of items // inside the folder. The items may also be either files or folders. -export declare type OdFolderObject = { +export type OdFolderObject = { '@odata.count': number '@odata.context': string '@odata.nextLink'?: string @@ -20,7 +20,7 @@ export declare type OdFolderObject = { }> } // A file object returned from the OneDrive API. This object may contain 'video' if the file is a video. -export declare type OdFileObject = { +export type OdFileObject = { '@microsoft.graph.downloadUrl': string '@odata.context': string name: string @@ -30,14 +30,16 @@ export declare type OdFileObject = { file: { mimeType: string; hashes: { quickXorHash: string; sha1Hash?: string; sha256Hash?: string } } image?: OdImageFile video?: OdVideoFile + 'thumbnails@odata.context'?: string + thumbnails?: Array } // A representation of a OneDrive image file. Some images do not return a width and height, so types are optional. -export declare type OdImageFile = { +export type OdImageFile = { width?: number height?: number } // A representation of a OneDrive video file. All fields are declared here, but we mainly use 'width' and 'height'. -export declare type OdVideoFile = { +export type OdVideoFile = { width: number height: number duration: number @@ -48,8 +50,14 @@ export declare type OdVideoFile = { audioFormat: string audioSamplesPerSecond: number } +export type OdThumbnail = { + id: string + large: { height: number; width: number; url: string } + medium: { height: number; width: number; url: string } + small: { height: number; width: number; url: string } +} // API response object for /api/search?q=. Likewise, this array of items may also contain either files or folders. -export declare type OdSearchResult = Array<{ +export type OdSearchResult = Array<{ id: string name: string file?: OdFileObject