From 10efcaddb4bc33644b259bc64e2eb1cc52a21c58 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 5 Oct 2021 12:23:34 +0530 Subject: [PATCH 01/69] added downloadFile util --- src/components/PhotoSwipe/PhotoSwipe.tsx | 21 +++++---------------- src/utils/file/index.ts | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index acbd5f16e..845d3cc08 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -7,16 +7,15 @@ import { addToFavorites, removeFromFavorites, } from 'services/collectionService'; -import { File, FILE_TYPE } from 'services/fileService'; +import { File } from 'services/fileService'; import constants from 'utils/strings/constants'; -import DownloadManger from 'services/downloadManager'; import exifr from 'exifr'; import Modal from 'react-bootstrap/Modal'; import Button from 'react-bootstrap/Button'; import Form from 'react-bootstrap/Form'; import styled from 'styled-components'; import events from './events'; -import { fileNameWithoutExtension, formatDateTime } from 'utils/file'; +import { downloadFile, formatDateTime } from 'utils/file'; import { FormCheck } from 'react-bootstrap'; import { prettyPrintExif } from 'utils/exif'; @@ -296,21 +295,11 @@ function PhotoSwipe(props: Iprops) { setShowInfo(true); }; - const downloadFile = async (file) => { + const downloadFileHelper = async (file) => { const { loadingBar } = props; - const a = document.createElement('a'); - a.style.display = 'none'; loadingBar.current.continuousStart(); - a.href = await DownloadManger.getFile(file); + await downloadFile(file); loadingBar.current.complete(); - if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) { - a.download = fileNameWithoutExtension(file.metadata.title) + '.zip'; - } else { - a.download = file.metadata.title; - } - document.body.appendChild(a); - a.click(); - a.remove(); }; const { id } = props; let { className } = props; @@ -344,7 +333,7 @@ function PhotoSwipe(props: Iprops) { className="pswp-custom download-btn" title={constants.DOWNLOAD} onClick={() => - downloadFile(photoSwipe.currItem) + downloadFileHelper(photoSwipe.currItem) } /> diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 66f0e3816..154639c32 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -11,6 +11,7 @@ import { import { decodeMotionPhoto } from 'services/motionPhotoService'; import { getMimeTypeFromBlob } from 'services/upload/readFileService'; import { EncryptionResult } from 'services/upload/uploadService'; +import DownloadManger from 'services/downloadManager'; import { logError } from 'utils/sentry'; import { User } from 'services/userService'; import CryptoWorker from 'utils/crypto'; @@ -36,6 +37,20 @@ export function downloadAsFile(filename: string, content: string) { a.remove(); } +export async function downloadFile(file) { + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = await DownloadManger.getFile(file); + if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) { + a.download = fileNameWithoutExtension(file.metadata.title) + '.zip'; + } else { + a.download = file.metadata.title; + } + document.body.appendChild(a); + a.click(); + a.remove(); +} + export function fileIsHEIC(mimeType: string) { return ( mimeType.toLowerCase().endsWith(TYPE_HEIC) || From 9c0dfde01d7051a7e6c0e997f9d5ab60ae1648a0 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 5 Oct 2021 12:24:21 +0530 Subject: [PATCH 02/69] added better download option for when playback failed --- src/components/PhotoFrame.tsx | 89 +++++++++-------------------------- src/pages/_app.tsx | 23 ++++++++- src/utils/photoFrame/index.ts | 15 ++++++ 3 files changed, 59 insertions(+), 68 deletions(-) create mode 100644 src/utils/photoFrame/index.ts diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index faeae4cff..66dc07f2f 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -18,7 +18,6 @@ import { VariableSizeList as List } from 'react-window'; import PhotoSwipe from 'components/PhotoSwipe/PhotoSwipe'; import { isInsideBox, isSameDay as isSameDayAnyYear } from 'utils/search'; import { SetDialogMessage } from './MessageDialog'; -import { CustomError } from 'utils/common/errorUtil'; import { GAP_BTW_TILES, DATE_CONTAINER_HEIGHT, @@ -30,10 +29,10 @@ import { import { fileIsArchived } from 'utils/file'; import { ALL_SECTION, ARCHIVE_SECTION } from './pages/gallery/Collections'; import { isSharedFile } from 'utils/file'; +import { isPlaybackPossible } from 'utils/photoFrame'; const NO_OF_PAGES = 2; const A_DAY = 24 * 60 * 60 * 1000; -const WAIT_FOR_VIDEO_PLAYBACK = 1 * 1000; interface TimeStampListItem { itemType: ITEM_TYPE; @@ -169,7 +168,6 @@ const PhotoFrame = ({ search, setSearchStats, deleted, - setDialogMessage, activeCollection, isSharedCollection, }: Props) => { @@ -225,21 +223,33 @@ const PhotoFrame = ({ setFiles(files); }; - const updateSrcUrl = (index: number, url: string) => { + const updateSrcUrl = async (index: number, url: string) => { files[index] = { ...files[index], - src: url, w: window.innerWidth, h: window.innerHeight, }; if (files[index].metadata.fileType === FILE_TYPE.VIDEO) { - files[index].html = ` + if (await isPlaybackPossible(url)) { + files[index].html = ` `; - delete files[index].src; + } else { + files[index].html = ` +
+ +
+ ${constants.VIDEO_PLAYBACK_FAILED_DOWNLOAD_INSTEAD} + Download +
+
+ `; + } + } else { + files[index].src = url; } setFiles(files); }; @@ -313,66 +323,11 @@ const PhotoFrame = ({ url = await DownloadManager.getFile(item, true); galleryContext.files.set(item.id, url); } - updateSrcUrl(item.dataIndex, url); - if (item.metadata.fileType === FILE_TYPE.VIDEO) { - try { - await new Promise((resolve, reject) => { - const video = document.createElement('video'); - video.addEventListener('timeupdate', function () { - clearTimeout(t); - resolve(null); - }); - video.preload = 'metadata'; - video.src = url; - video.currentTime = 3; - const t = setTimeout(() => { - reject( - Error( - `${CustomError.VIDEO_PLAYBACK_FAILED} err: wait time exceeded` - ) - ); - }, WAIT_FOR_VIDEO_PLAYBACK); - }); - item.html = ` - - `; - delete item.src; - } catch (e) { - const downloadFile = async () => { - const a = document.createElement('a'); - a.style.display = 'none'; - a.href = url; - a.download = item.metadata.title; - document.body.appendChild(a); - a.click(); - a.remove(); - setOpen(false); - }; - setDialogMessage({ - title: constants.VIDEO_PLAYBACK_FAILED, - content: - constants.VIDEO_PLAYBACK_FAILED_DOWNLOAD_INSTEAD, - staticBackdrop: true, - proceed: { - text: constants.DOWNLOAD, - action: downloadFile, - variant: 'success', - }, - close: { - text: constants.CLOSE, - action: () => setOpen(false), - }, - }); - return; - } - } else { - item.src = url; - } - item.w = window.innerWidth; - item.h = window.innerHeight; + await updateSrcUrl(item.dataIndex, url); + item.html = files[item.dataIndex].html; + item.src = files[item.dataIndex].src; + item.w = files[item.dataIndex].w; + item.h = files[item.dataIndex].h; try { instance.invalidateCurrItems(); instance.updateSize(true); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 00b11549a..7680cabcf 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -79,12 +79,33 @@ const GlobalStyles = createGlobalStyle` height: 100%; } - .video-loading > div { + .video-loading > div.spinner-border { position: relative; top: -50vh; left: 50vw; } + + + .video-loading > div.download-message { + position: relative; + top: -60vh; + left: 0; + height: 20vh; + padding:2vh 0; + background-color: #151414; + color:#ddd; + display: flex; + flex-direction:column; + align-items: center; + justify-content: space-around; + opacity: 0.8; + font-size:20px; + } + .download-message > a{ + width: 130px; + } + :root { --primary: #e26f99, }; diff --git a/src/utils/photoFrame/index.ts b/src/utils/photoFrame/index.ts new file mode 100644 index 000000000..fdaeb8557 --- /dev/null +++ b/src/utils/photoFrame/index.ts @@ -0,0 +1,15 @@ +const WAIT_FOR_VIDEO_PLAYBACK = 1 * 1000; + +export async function isPlaybackPossible(url: string): Promise { + return await new Promise((resolve) => { + const t = setTimeout(() => { + resolve(false); + }, WAIT_FOR_VIDEO_PLAYBACK); + const video = document.createElement('video'); + video.addEventListener('canplay', function () { + clearTimeout(t); + resolve(true); + }); + video.src = url; + }); +} From 0d62c1e67f4452e4bec67cb319399ea7399435de Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 5 Oct 2021 12:43:29 +0530 Subject: [PATCH 03/69] make banner thinner --- src/pages/_app.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 7680cabcf..eff5d1866 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -91,7 +91,7 @@ const GlobalStyles = createGlobalStyle` position: relative; top: -60vh; left: 0; - height: 20vh; + height: 16vh; padding:2vh 0; background-color: #151414; color:#ddd; From 2edce610f832945f87ac0302ef761f146e089233 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 5 Oct 2021 14:08:26 +0530 Subject: [PATCH 04/69] fix download file name bug --- src/components/PhotoFrame.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index 66dc07f2f..951859102 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -243,7 +243,7 @@ const PhotoFrame = ({ `; From f6af345421a32675ce787fccefe9d67aadabdf2b Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 5 Oct 2021 15:18:05 +0530 Subject: [PATCH 05/69] remove states for isSharedCollectionActive and isFavCollectionActive. Interfering with colletion Chip auto scroll to searched item --- src/pages/gallery/index.tsx | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 08beb7d24..59b307568 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -184,11 +184,6 @@ export default function Gallery() { useState>(); const [activeCollection, setActiveCollection] = useState(undefined); - const [isSharedCollectionActive, setIsSharedCollectionActive] = - useState(false); - - const [isFavCollectionActive, setIsFavCollectionActive] = useState(false); - useEffect(() => { const key = getKey(SESSION_KEYS.ENCRYPTION_KEY); if (!key) { @@ -246,14 +241,6 @@ export default function Gallery() { } const href = `/gallery${collectionURL}`; router.push(href, undefined, { shallow: true }); - - setIsSharedCollectionActive( - isSharedCollection(activeCollection, collections) - ); - - setIsFavCollectionActive( - isFavoriteCollection(activeCollection, collections) - ); }, [activeCollection]); const syncWithRemote = async (force = false, silent = false) => { @@ -623,7 +610,10 @@ export default function Gallery() { deleted={deleted} setDialogMessage={setDialogMessage} activeCollection={activeCollection} - isSharedCollection={isSharedCollectionActive} + isSharedCollection={isSharedCollection( + activeCollection, + collections + )} /> {selected.count > 0 && selected.collectionID === activeCollection && ( @@ -651,7 +641,10 @@ export default function Gallery() { count={selected.count} clearSelection={clearSelection} activeCollection={activeCollection} - isFavoriteCollection={isFavCollectionActive} + isFavoriteCollection={isFavoriteCollection( + activeCollection, + collections + )} /> )} From 52fe27263c9215226c5c73cdbcdf639634428879 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 11:24:21 +0530 Subject: [PATCH 06/69] search in all section instead of the collection user opened search on --- src/components/SearchBar.tsx | 6 +++--- src/components/pages/gallery/Collections.tsx | 2 +- src/pages/gallery/index.tsx | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index e854e2639..3d12b0322 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -177,12 +177,12 @@ export default function SearchBar(props: Props) { break; case SuggestionType.COLLECTION: props.setActiveCollection(selectedOption.value as number); - resetSearch(true); + setValue(null); break; } }; - const resetSearch = async (force?: boolean) => { - if (props.isOpen || force) { + const resetSearch = () => { + if (props.isOpen) { props.loadingBar.current?.continuousStart(); props.setSearch({}); setTimeout(() => { diff --git a/src/components/pages/gallery/Collections.tsx b/src/components/pages/gallery/Collections.tsx index 7277f2c18..05bb5610e 100644 --- a/src/components/pages/gallery/Collections.tsx +++ b/src/components/pages/gallery/Collections.tsx @@ -119,7 +119,7 @@ export default function Collections(props: CollectionProps) { useEffect(() => { updateScrollObj(); - }, [collectionWrapperRef.current]); + }, [collectionWrapperRef.current, props.searchMode]); useEffect(() => { if (!collectionWrapperRef?.current) { diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 08beb7d24..4c27607d8 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -492,6 +492,7 @@ export default function Gallery() { }; const updateSearch = (search: Search) => { + setActiveCollection(ALL_SECTION); setSearch(search); setSearchStats(null); }; From afbcda58928bc685442401faadadfebc4f1827ab Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 13:52:25 +0530 Subject: [PATCH 07/69] renamed searchMode to isInSearchMode state --- src/components/PhotoFrame.tsx | 10 +++++----- src/components/pages/gallery/Collections.tsx | 6 +++--- src/pages/gallery/index.tsx | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index faeae4cff..403571317 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -146,7 +146,7 @@ interface Props { isFirstLoad; openFileUploader; loadingBar; - searchMode: boolean; + isInSearchMode: boolean; search: Search; setSearchStats: setSearchStats; deleted?: number[]; @@ -165,7 +165,7 @@ const PhotoFrame = ({ isFirstLoad, openFileUploader, loadingBar, - searchMode, + isInSearchMode, search, setSearchStats, deleted, @@ -181,7 +181,7 @@ const PhotoFrame = ({ const listRef = useRef(null); useEffect(() => { - if (searchMode) { + if (isInSearchMode) { setSearchStats({ resultCount: filteredData.length, timeTaken: (Date.now() - startTime) / 1000, @@ -515,7 +515,7 @@ const PhotoFrame = ({ return ( <> - {!isFirstLoad && files.length === 0 && !searchMode ? ( + {!isFirstLoad && files.length === 0 && !isInSearchMode ? (
@@ -635,7 +635,7 @@ const PhotoFrame = ({ return sum; })(); files.length < 30 && - !searchMode && + !isInSearchMode && timeStampList.push({ itemType: ITEM_TYPE.BANNER, banner: ( diff --git a/src/components/pages/gallery/Collections.tsx b/src/components/pages/gallery/Collections.tsx index 05bb5610e..5131e2a25 100644 --- a/src/components/pages/gallery/Collections.tsx +++ b/src/components/pages/gallery/Collections.tsx @@ -35,7 +35,7 @@ interface CollectionProps { syncWithRemote: () => Promise; setCollectionNamerAttributes: SetCollectionNamerAttributes; startLoadingBar: () => void; - searchMode: boolean; + isInSearchMode: boolean; collectionFilesCount: Map; } @@ -119,7 +119,7 @@ export default function Collections(props: CollectionProps) { useEffect(() => { updateScrollObj(); - }, [collectionWrapperRef.current, props.searchMode]); + }, [collectionWrapperRef.current, props.isInSearchMode]); useEffect(() => { if (!collectionWrapperRef?.current) { @@ -184,7 +184,7 @@ export default function Collections(props: CollectionProps) { }; return ( - !props.searchMode && ( + !props.isInSearchMode && ( <> Date: Thu, 7 Oct 2021 14:02:07 +0530 Subject: [PATCH 08/69] remember prevCollection before search and redirect to it after search --- src/pages/gallery/index.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 6ba61d431..9271979a2 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -106,6 +106,7 @@ export type setSearchStats = React.Dispatch>; export type Search = { date?: DateValue; location?: Bbox; + prevCollection?: number; }; export interface SearchStats { resultCount: number; @@ -491,9 +492,11 @@ export default function Gallery() { } }; - const updateSearch = (search: Search) => { - setActiveCollection(ALL_SECTION); - setSearch(search); + const updateSearch = (newSearch: Search) => { + const prevCollection = search.prevCollection ?? ALL_SECTION; + const currentCollection = activeCollection; + setActiveCollection(prevCollection); + setSearch({ ...newSearch, prevCollection: currentCollection }); setSearchStats(null); }; From afce424396d66c4f93476389741c2bf8dfa50d2b Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 14:44:39 +0530 Subject: [PATCH 09/69] hide collection instead of removing it form dom in searhch mode --- src/components/pages/gallery/Collections.tsx | 202 +++++++++---------- 1 file changed, 100 insertions(+), 102 deletions(-) diff --git a/src/components/pages/gallery/Collections.tsx b/src/components/pages/gallery/Collections.tsx index 5131e2a25..db2a417bb 100644 --- a/src/components/pages/gallery/Collections.tsx +++ b/src/components/pages/gallery/Collections.tsx @@ -87,6 +87,11 @@ const Chip = styled.button<{ active: boolean }>` } `; +const Hider = styled.div<{ hide: boolean }>` + opacity: ${(props) => (props.hide ? '0' : '100')}; + height: ${(props) => (props.hide ? '0' : 'auto')}; +`; + export default function Collections(props: CollectionProps) { const { activeCollection, collections, setActiveCollection } = props; const [selectedCollectionID, setSelectedCollectionID] = @@ -184,111 +189,104 @@ export default function Collections(props: CollectionProps) { }; return ( - !props.isInSearchMode && ( - <> - setCollectionShareModalView(false)} - collection={getSelectedCollection( - selectedCollectionID, - props.collections + + setCollectionShareModalView(false)} + collection={getSelectedCollection( + selectedCollectionID, + props.collections + )} + syncWithRemote={props.syncWithRemote} + /> + + + {scrollObj.scrollLeft > 0 && ( + )} - syncWithRemote={props.syncWithRemote} - /> - - - {scrollObj.scrollLeft > 0 && ( - + + {constants.ALL} +
- )} - - - {constants.ALL} -
- - {sortCollections( - collections, - props.collectionAndTheirLatestFile, - collectionSortBy - ).map((item) => ( - - - {item.name} - {item.type !== - CollectionType.favorites && - item.owner.id === user?.id ? ( - - - setSelectedCollectionID( - item.id - ) - } - /> - - ) : ( -
+ {sortCollections( + collections, + props.collectionAndTheirLatestFile, + collectionSortBy + ).map((item) => ( + + + {item.name} + {item.type !== CollectionType.favorites && + item.owner.id === user?.id ? ( + + + setSelectedCollectionID( + item.id + ) + } /> - )} - - - ))} - - {constants.ARCHIVE} -
- - - {scrollObj.scrollLeft < - scrollObj.scrollWidth - scrollObj.clientWidth && ( - + ) : ( +
+ )} + + + ))} + + {constants.ARCHIVE} +
- )} - - - - - ) + + + {scrollObj.scrollLeft < + scrollObj.scrollWidth - scrollObj.clientWidth && ( + + )} + + + + ); } From 57ff20029f94f017c4ef85f07a298daf1a897b47 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 15:02:31 +0530 Subject: [PATCH 10/69] set intial prevCollection to ALL_SECTION to avoid null check on searchUpdate --- src/pages/gallery/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 9271979a2..ab5e3f1f5 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -160,6 +160,7 @@ export default function Gallery() { const [search, setSearch] = useState({ date: null, location: null, + prevCollection: ALL_SECTION, }); const [uploadInProgress, setUploadInProgress] = useState(false); const { @@ -493,7 +494,7 @@ export default function Gallery() { }; const updateSearch = (newSearch: Search) => { - const prevCollection = search.prevCollection ?? ALL_SECTION; + const prevCollection = search.prevCollection; const currentCollection = activeCollection; setActiveCollection(prevCollection); setSearch({ ...newSearch, prevCollection: currentCollection }); From 950adae9b3ca9870fc5c6fa0999d1cc297a87ea0 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Wed, 6 Oct 2021 11:24:05 +0530 Subject: [PATCH 11/69] Adds file search by name --- src/components/PhotoFrame.tsx | 3 +++ src/components/SearchBar.tsx | 39 +++++++++++++++++++++++++----- src/components/icons/ImageIcon.tsx | 21 ++++++++++++++++ src/components/icons/VideoIcon.tsx | 20 +++++++++++++++ src/pages/gallery/index.tsx | 3 +++ src/services/searchService.ts | 11 +++++++++ 6 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/components/icons/ImageIcon.tsx create mode 100644 src/components/icons/VideoIcon.tsx diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index 403571317..a7598b10d 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -187,6 +187,9 @@ const PhotoFrame = ({ timeTaken: (Date.now() - startTime) / 1000, }); } + if (search.fileIndex || search.fileIndex === 0) { + onThumbnailClick(search.fileIndex)(); + } }, [search]); useEffect(() => { diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 3d12b0322..2463ffa75 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -10,6 +10,7 @@ import { getYearSuggestion, parseHumanDate, searchCollection, + searchFiles, searchLocation, } from 'services/searchService'; import { getFormattedDate } from 'utils/search'; @@ -20,6 +21,9 @@ import SearchIcon from './icons/SearchIcon'; import CrossIcon from './icons/CrossIcon'; import { Collection } from 'services/collectionService'; import CollectionIcon from './icons/CollectionIcon'; +import { File, FILE_TYPE } from 'services/fileService'; +import ImageIcon from './icons/ImageIcon'; +import VideoIcon from './icons/VideoIcon'; const Wrapper = styled.div<{ isDisabled: boolean; isOpen: boolean }>` position: fixed; @@ -74,6 +78,8 @@ export enum SuggestionType { DATE, LOCATION, COLLECTION, + IMAGE, + VIDEO, } export interface DateValue { date?: number; @@ -94,6 +100,7 @@ interface Props { searchStats: SearchStats; collections: Collection[]; setActiveCollection: (id: number) => void; + files: File[]; } export default function SearchBar(props: Props) { const [value, setValue] = useState(null); @@ -112,14 +119,14 @@ export default function SearchBar(props: Props) { if (!searchPhrase?.length) { return []; } - const option = [ + const options = [ ...getHolidaySuggestion(searchPhrase), ...getYearSuggestion(searchPhrase), ]; const searchedDates = parseHumanDate(searchPhrase); - option.push( + options.push( ...searchedDates.map((searchedDate) => ({ type: SuggestionType.DATE, value: searchedDate, @@ -131,7 +138,7 @@ export default function SearchBar(props: Props) { searchPhrase, props.collections ); - option.push( + options.push( ...collectionResults.map( (searchResult) => ({ @@ -141,8 +148,20 @@ export default function SearchBar(props: Props) { } as Suggestion) ) ); + const fileResults = searchFiles(searchPhrase, props.files); + options.push( + ...fileResults.map((file) => ({ + type: + file.type === FILE_TYPE.IMAGE + ? SuggestionType.IMAGE + : SuggestionType.VIDEO, + value: file.index, + label: file.title, + })) + ); + const locationResults = await searchLocation(searchPhrase); - option.push( + options.push( ...locationResults.map( (searchResult) => ({ @@ -152,7 +171,7 @@ export default function SearchBar(props: Props) { } as Suggestion) ) ); - return option; + return options; }; const getOptions = debounce(getAutoCompleteSuggestions, 250); @@ -161,7 +180,6 @@ export default function SearchBar(props: Props) { if (!selectedOption) { return; } - switch (selectedOption.type) { case SuggestionType.DATE: props.setSearch({ @@ -179,6 +197,11 @@ export default function SearchBar(props: Props) { props.setActiveCollection(selectedOption.value as number); setValue(null); break; + case SuggestionType.IMAGE: + case SuggestionType.VIDEO: + props.setSearch({ fileIndex: selectedOption.value as number }); + setValue(null); + break; } }; const resetSearch = () => { @@ -205,6 +228,10 @@ export default function SearchBar(props: Props) { return ; case SuggestionType.COLLECTION: return ; + case SuggestionType.IMAGE: + return ; + case SuggestionType.VIDEO: + return ; default: return ; } diff --git a/src/components/icons/ImageIcon.tsx b/src/components/icons/ImageIcon.tsx new file mode 100644 index 000000000..6e2d6f8de --- /dev/null +++ b/src/components/icons/ImageIcon.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +export default function ImageIcon(props) { + return ( + + + + + ); +} + +ImageIcon.defaultProps = { + height: 24, + width: 24, + viewBox: '0 0 24 24', +}; diff --git a/src/components/icons/VideoIcon.tsx b/src/components/icons/VideoIcon.tsx new file mode 100644 index 000000000..510367881 --- /dev/null +++ b/src/components/icons/VideoIcon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +export default function VideoIcon(props) { + return ( + + + + ); +} + +VideoIcon.defaultProps = { + height: 24, + width: 24, + viewBox: '0 0 24 24', +}; diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index ab5e3f1f5..fa615031d 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -107,6 +107,7 @@ export type Search = { date?: DateValue; location?: Bbox; prevCollection?: number; + fileIndex?: number; }; export interface SearchStats { resultCount: number; @@ -161,6 +162,7 @@ export default function Gallery() { date: null, location: null, prevCollection: ALL_SECTION, + fileIndex: null, }); const [uploadInProgress, setUploadInProgress] = useState(false); const { @@ -548,6 +550,7 @@ export default function Gallery() { loadingBar={loadingBar} isFirstFetch={isFirstFetch} collections={collections} + files={files} setActiveCollection={setActiveCollection} setSearch={updateSearch} searchStats={searchStats} diff --git a/src/services/searchService.ts b/src/services/searchService.ts index d5832fd84..393d07fb0 100644 --- a/src/services/searchService.ts +++ b/src/services/searchService.ts @@ -4,6 +4,7 @@ import { getToken } from 'utils/common/key'; import { DateValue, Suggestion, SuggestionType } from 'components/SearchBar'; import HTTPService from './HTTPService'; import { Collection } from './collectionService'; +import { File } from './fileService'; const ENDPOINT = getEndpoint(); const DIGITS = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']); @@ -110,3 +111,13 @@ export function searchCollection( collection.name.toLowerCase().includes(searchPhrase) ); } + +export function searchFiles(searchPhrase: string, files: File[]) { + return files + .map((file, idx) => ({ + title: file.metadata.title, + index: idx, + type: file.metadata.fileType, + })) + .filter(({ title }) => title.toLowerCase().includes(searchPhrase)); +} From 828844151c57eb89bed1a05d75ca109af4e67bcd Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Wed, 6 Oct 2021 12:51:17 +0530 Subject: [PATCH 12/69] show only four file search results --- src/services/searchService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/searchService.ts b/src/services/searchService.ts index 393d07fb0..d90cfddf2 100644 --- a/src/services/searchService.ts +++ b/src/services/searchService.ts @@ -119,5 +119,6 @@ export function searchFiles(searchPhrase: string, files: File[]) { index: idx, type: file.metadata.fileType, })) - .filter(({ title }) => title.toLowerCase().includes(searchPhrase)); + .filter(({ title }) => title.toLowerCase().includes(searchPhrase)) + .slice(0, 4); } From 6d8028ae991799613ebf5a1b8a309db43d3a2272 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Wed, 6 Oct 2021 12:51:46 +0530 Subject: [PATCH 13/69] find filteredData index and then open that item --- src/components/PhotoFrame.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index a7598b10d..28de2cdc6 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -188,7 +188,12 @@ const PhotoFrame = ({ }); } if (search.fileIndex || search.fileIndex === 0) { - onThumbnailClick(search.fileIndex)(); + const filteredDataIdx = filteredData.findIndex( + (data) => data.dataIndex === search.fileIndex + ); + if (filteredDataIdx) { + onThumbnailClick(filteredDataIdx)(); + } } }, [search]); From 9afd224a231ad58c9ee1dddd10d9ab23b90312b2 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 11:17:22 +0530 Subject: [PATCH 14/69] dont show shared files in options --- src/services/searchService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/searchService.ts b/src/services/searchService.ts index d90cfddf2..d24e2d239 100644 --- a/src/services/searchService.ts +++ b/src/services/searchService.ts @@ -5,6 +5,8 @@ import { DateValue, Suggestion, SuggestionType } from 'components/SearchBar'; import HTTPService from './HTTPService'; import { Collection } from './collectionService'; import { File } from './fileService'; +import { User } from './userService'; +import { getData, LS_KEYS } from 'utils/storage/localStorage'; const ENDPOINT = getEndpoint(); const DIGITS = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']); @@ -113,7 +115,9 @@ export function searchCollection( } export function searchFiles(searchPhrase: string, files: File[]) { + const user: User = getData(LS_KEYS.USER) ?? {}; return files + .filter((file) => file.ownerID === user.id) .map((file, idx) => ({ title: file.metadata.title, index: idx, From 9fa1c1fc0988f049fb025ea91aedfbfdb2556f65 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 11:42:54 +0530 Subject: [PATCH 15/69] fix owner photo filtering --- src/services/searchService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/searchService.ts b/src/services/searchService.ts index d24e2d239..0469ebf21 100644 --- a/src/services/searchService.ts +++ b/src/services/searchService.ts @@ -117,12 +117,13 @@ export function searchCollection( export function searchFiles(searchPhrase: string, files: File[]) { const user: User = getData(LS_KEYS.USER) ?? {}; return files - .filter((file) => file.ownerID === user.id) .map((file, idx) => ({ title: file.metadata.title, index: idx, type: file.metadata.fileType, + ownerID: file.ownerID, })) + .filter((file) => file.ownerID === user.id) .filter(({ title }) => title.toLowerCase().includes(searchPhrase)) .slice(0, 4); } From cbe1d8ce88fae3c302820959d4b7e5f957f6a713 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 12:49:37 +0530 Subject: [PATCH 16/69] show only one instance of a file --- src/components/PhotoFrame.tsx | 2 +- src/services/searchService.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index 28de2cdc6..750ef4cf0 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -191,7 +191,7 @@ const PhotoFrame = ({ const filteredDataIdx = filteredData.findIndex( (data) => data.dataIndex === search.fileIndex ); - if (filteredDataIdx) { + if (filteredDataIdx || filteredDataIdx === 0) { onThumbnailClick(filteredDataIdx)(); } } diff --git a/src/services/searchService.ts b/src/services/searchService.ts index 0469ebf21..34c5c06b9 100644 --- a/src/services/searchService.ts +++ b/src/services/searchService.ts @@ -116,14 +116,22 @@ export function searchCollection( export function searchFiles(searchPhrase: string, files: File[]) { const user: User = getData(LS_KEYS.USER) ?? {}; + const idSet = new Set(); return files .map((file, idx) => ({ title: file.metadata.title, index: idx, type: file.metadata.fileType, ownerID: file.ownerID, + id: file.id, })) - .filter((file) => file.ownerID === user.id) + .filter((file) => { + if (file.ownerID === user.id && !idSet.has(file.id)) { + idSet.add(file.id); + return true; + } + return false; + }) .filter(({ title }) => title.toLowerCase().includes(searchPhrase)) .slice(0, 4); } From 76351c38ab82c9a9c47b9b7621d967007d7cf2ec Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 20:40:41 +0530 Subject: [PATCH 17/69] remove prev collection redirecting --- src/pages/gallery/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index ab5e3f1f5..50a3f1977 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -494,10 +494,8 @@ export default function Gallery() { }; const updateSearch = (newSearch: Search) => { - const prevCollection = search.prevCollection; - const currentCollection = activeCollection; - setActiveCollection(prevCollection); - setSearch({ ...newSearch, prevCollection: currentCollection }); + setActiveCollection(ALL_SECTION); + setSearch(newSearch); setSearchStats(null); }; From 515266bc93133e00570ebf283eb4b5062a750f17 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 7 Oct 2021 20:46:21 +0530 Subject: [PATCH 18/69] remove prev collection from search --- src/pages/gallery/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 50a3f1977..92d25ae2f 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -106,7 +106,6 @@ export type setSearchStats = React.Dispatch>; export type Search = { date?: DateValue; location?: Bbox; - prevCollection?: number; }; export interface SearchStats { resultCount: number; @@ -160,7 +159,6 @@ export default function Gallery() { const [search, setSearch] = useState({ date: null, location: null, - prevCollection: ALL_SECTION, }); const [uploadInProgress, setUploadInProgress] = useState(false); const { From 3de8b0bfbea9c7197142ae445a06108d6da8d69a Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Fri, 8 Oct 2021 13:20:54 +0530 Subject: [PATCH 19/69] generate jpeg thumbnail with 512*512 resolution and jpeg format --- src/services/ffmpegService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/ffmpegService.ts b/src/services/ffmpegService.ts index 52522a6af..13b7bde7e 100644 --- a/src/services/ffmpegService.ts +++ b/src/services/ffmpegService.ts @@ -45,7 +45,7 @@ class FFmpegService { async function generateThumbnailHelper(ffmpeg: FFmpeg, file: File) { try { const inputFileName = `${Date.now().toString}-${file.name}`; - const thumbFileName = `${Date.now().toString}-thumb.png`; + const thumbFileName = `${Date.now().toString}-thumb.jpeg`; ffmpeg.FS( 'writeFile', inputFileName, @@ -62,6 +62,7 @@ async function generateThumbnailHelper(ffmpeg: FFmpeg, file: File) { `00:00:0${seekTime.toFixed(3)}`, '-vframes', '1', + '-vf scale=512:512', thumbFileName ); thumb = ffmpeg.FS('readFile', thumbFileName); From 11eb962f7944a7e956853ab113d1199fd550baab Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Fri, 8 Oct 2021 13:46:11 +0530 Subject: [PATCH 20/69] compress thumbnail generated from ffmpeg using canvas method --- src/services/upload/thumbnailService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index e63df1f8a..434de1461 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -26,7 +26,12 @@ export async function generateThumbnail( } else { try { const thumb = await FFmpegService.generateThumbnail(file); - return { thumbnail: thumb, hasStaticThumbnail: false }; + const dummyImageFile = new File([thumb], file.name); + canvas = await generateImageThumbnail( + worker, + dummyImageFile, + isHEIC + ); } catch (e) { canvas = await generateVideoThumbnail(file); } From b7c6eb8a2f28518500e8d59530791512694632ee Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Fri, 8 Oct 2021 19:56:06 +0530 Subject: [PATCH 21/69] updated _downloadThumb into getThumbnail to get uint8array as output --- src/services/downloadManager.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index af2c72e92..0de57e8e6 100644 --- a/src/services/downloadManager.ts +++ b/src/services/downloadManager.ts @@ -43,6 +43,19 @@ class DownloadManager { thumbnailCache: Cache, file: File ) => { + const thumb = await this.getThumbnail(token, file); + try { + await thumbnailCache.put( + file.id.toString(), + new Response(new Blob([thumb])) + ); + } catch (e) { + // TODO: handle storage full exception. + } + return URL.createObjectURL(new Blob([thumb])); + }; + + getThumbnail = async (token: string, file: File) => { const resp = await HTTPService.get( getThumbnailUrl(file.id), null, @@ -50,20 +63,12 @@ class DownloadManager { { responseType: 'arraybuffer' } ); const worker = await new CryptoWorker(); - const decrypted: any = await worker.decryptThumbnail( + const decrypted: Uint8Array = await worker.decryptThumbnail( new Uint8Array(resp.data), await worker.fromB64(file.thumbnail.decryptionHeader), file.key ); - try { - await thumbnailCache.put( - file.id.toString(), - new Response(new Blob([decrypted])) - ); - } catch (e) { - // TODO: handle storage full exception. - } - return URL.createObjectURL(new Blob([decrypted])); + return decrypted; }; getFile = async (file: File, forPreview = false) => { From d14217400af045657663eca8304fddfeee7620a3 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Fri, 8 Oct 2021 19:56:49 +0530 Subject: [PATCH 22/69] added migrate service to update large thumbnails --- src/services/migrate/index.ts | 130 ++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/services/migrate/index.ts diff --git a/src/services/migrate/index.ts b/src/services/migrate/index.ts new file mode 100644 index 000000000..fc74535d4 --- /dev/null +++ b/src/services/migrate/index.ts @@ -0,0 +1,130 @@ +import downloadManager from 'services/downloadManager'; +import { File, fileAttribute, FILE_TYPE } from 'services/fileService'; +import { + generateThumbnail, + MAX_THUMBNAIL_SIZE, +} from 'services/upload/thumbnailService'; +import { getToken } from 'utils/common/key'; +import { logError } from 'utils/sentry'; +import { getEndpoint } from 'utils/common/apiUtil'; +import HTTPService from 'services/HTTPService'; +import CryptoWorker from 'utils/crypto'; +import { convertToHumanReadable } from 'utils/billingUtil'; +import uploadHttpClient from 'services/upload/uploadHttpClient'; +import { EncryptionResult, UploadURL } from 'services/upload/uploadService'; + +const ENDPOINT = getEndpoint(); + +export async function getLargeThumbnailFiles() { + try { + const token = getToken(); + if (!token) { + return; + } + const resp = await HTTPService.get( + `${ENDPOINT}/files/large-thumbnails`, + { + threshold: 2 * MAX_THUMBNAIL_SIZE, + }, + { + 'X-Auth-Token': token, + } + ); + return resp.data.largeThumbnailFiles as number[]; + } catch (e) { + logError(e, 'failed to get large thumbnail files'); + throw e; + } +} +export async function regenerateThumbnail(files: File[]) { + try { + const token = getToken(); + const worker = await new CryptoWorker(); + const largeThumbnailFileIDs = new Set( + (await getLargeThumbnailFiles()) ?? [] + ); + const largeThumbnailFiles = files.filter((file) => + largeThumbnailFileIDs.has(file.id) + ); + const uploadURLs: UploadURL[] = []; + uploadHttpClient.fetchUploadURLs( + largeThumbnailFiles.length, + uploadURLs + ); + for (const file of largeThumbnailFiles) { + const originalThumbnail = await downloadManager.getThumbnail( + token, + file + ); + const dummyImageFile = new globalThis.File( + [originalThumbnail], + file.metadata.title + ); + const { thumbnail: newThumbnail } = await generateThumbnail( + worker, + dummyImageFile, + FILE_TYPE.IMAGE, + false + ); + const newUploadedThumbnail = await uploadThumbnail( + worker, + file.key, + newThumbnail, + uploadURLs.pop() + ); + await updateThumbnail(file.id, newUploadedThumbnail); + console.log( + URL.createObjectURL(new Blob([newThumbnail])), + convertToHumanReadable(newThumbnail.length) + ); + } + } catch (e) { + logError(e, 'failed to regenerate thumbnail'); + } +} + +export async function uploadThumbnail( + worker, + fileKey: string, + updatedThumbnail: Uint8Array, + uploadURL: UploadURL +): Promise { + const { file: encryptedThumbnail }: EncryptionResult = + await worker.encryptThumbnail(updatedThumbnail, fileKey); + + const thumbnailObjectKey = await uploadHttpClient.putFile( + uploadURL, + encryptedThumbnail.encryptedData as Uint8Array, + () => {} + ); + return { + objectKey: thumbnailObjectKey, + decryptionHeader: encryptedThumbnail.decryptionHeader, + }; +} + +export async function updateThumbnail( + fileID: number, + newThumbnail: fileAttribute +) { + try { + const token = getToken(); + if (!token) { + return; + } + await HTTPService.put( + `${ENDPOINT}/files/thumbnail`, + { + fileID: fileID, + thumbnail: newThumbnail, + }, + null, + { + 'X-Auth-Token': token, + } + ); + } catch (e) { + logError(e, 'failed to update thumbnail'); + throw e; + } +} From b399c50e652f87bf813e0c9a9cc5811d59aa789c Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Fri, 8 Oct 2021 19:57:38 +0530 Subject: [PATCH 23/69] fix MAX_THUMBNAIL_SIZE name typo and exported it --- src/services/upload/thumbnailService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 434de1461..9d844ca71 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -6,7 +6,7 @@ import FFmpegService from 'services/ffmpegService'; const THUMBNAIL_HEIGHT = 720; const MAX_ATTEMPTS = 3; -const MIN_THUMBNAIL_SIZE = 50000; +export const MAX_THUMBNAIL_SIZE = 50000; const WAIT_TIME_THUMBNAIL_GENERATION = 10 * 1000; @@ -171,7 +171,7 @@ export async function generateVideoThumbnail(file: globalThis.File) { } async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { - let thumbnailBlob = null; + let thumbnailBlob: Blob = null; let attempts = 0; let quality = 1; @@ -189,7 +189,7 @@ async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { attempts++; quality /= 2; } while ( - thumbnailBlob.size > MIN_THUMBNAIL_SIZE && + thumbnailBlob.size > MAX_THUMBNAIL_SIZE && attempts <= MAX_ATTEMPTS ); From 3f1c680df3c7ffd89583790b347ef1bf12a031ba Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Fri, 8 Oct 2021 19:58:03 +0530 Subject: [PATCH 24/69] print the root error in dev environment --- src/utils/sentry/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/sentry/index.ts b/src/utils/sentry/index.ts index fb889439d..7dab861b6 100644 --- a/src/utils/sentry/index.ts +++ b/src/utils/sentry/index.ts @@ -9,7 +9,7 @@ export const logError = ( ) => { const err = errorWithContext(e, msg); if (!process.env.NEXT_PUBLIC_SENTRY_ENV) { - console.log(err); + console.log(e); } Sentry.captureException(err, { level: Sentry.Severity.Info, From c095031b8e817873c204059bababcd8f535c9a7e Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Sat, 9 Oct 2021 15:19:33 +0530 Subject: [PATCH 25/69] add newly created favorite collection to localCollections --- src/services/collectionService.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/services/collectionService.ts b/src/services/collectionService.ts index 7ec6e7587..d7738116f 100644 --- a/src/services/collectionService.ts +++ b/src/services/collectionService.ts @@ -23,7 +23,6 @@ export enum CollectionType { } const COLLECTION_UPDATION_TIME = 'collection-updation-time'; -const FAV_COLLECTION = 'fav-collection'; const COLLECTIONS = 'collections'; export interface Collection { @@ -340,7 +339,11 @@ export const addToFavorites = async (file: File) => { 'Favorites', CollectionType.favorites ); - await localForage.setItem(FAV_COLLECTION, favCollection); + const localCollections = await getLocalCollections(); + await localForage.setItem(COLLECTIONS, [ + ...localCollections, + favCollection, + ]); } await addToCollection(favCollection, [file]); } catch (e) { From 6262b6e781e3cf5b14b7e85bc3e5ac1976f7fec7 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Sat, 9 Oct 2021 15:38:30 +0530 Subject: [PATCH 26/69] send rootCause message to sentry --- src/utils/sentry/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/sentry/index.ts b/src/utils/sentry/index.ts index fb889439d..97743da27 100644 --- a/src/utils/sentry/index.ts +++ b/src/utils/sentry/index.ts @@ -9,7 +9,7 @@ export const logError = ( ) => { const err = errorWithContext(e, msg); if (!process.env.NEXT_PUBLIC_SENTRY_ENV) { - console.log(err); + console.log(e); } Sentry.captureException(err, { level: Sentry.Severity.Info, @@ -17,6 +17,7 @@ export const logError = ( contexts: { ...(info && { info: info, + rootCause: { message: e?.message }, }), }, }); From dbbe5a1d9c3d14c89533f67ddfda72ddd8bfbfe0 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Sat, 9 Oct 2021 16:23:22 +0530 Subject: [PATCH 27/69] always send root cause message --- src/utils/sentry/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/sentry/index.ts b/src/utils/sentry/index.ts index 97743da27..aef44d4e5 100644 --- a/src/utils/sentry/index.ts +++ b/src/utils/sentry/index.ts @@ -17,8 +17,8 @@ export const logError = ( contexts: { ...(info && { info: info, - rootCause: { message: e?.message }, }), + rootCause: { message: e?.message }, }, }); }; From 6b25555044efb1b356bce14685232557192431c6 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 11 Oct 2021 10:30:36 +0530 Subject: [PATCH 28/69] added fix thumbnail modal --- src/components/FixLargeThumbnail.tsx | 108 +++++++++++++++++++++++++ src/components/Sidebar.tsx | 13 +++ src/utils/strings/englishConstants.tsx | 1 + 3 files changed, 122 insertions(+) create mode 100644 src/components/FixLargeThumbnail.tsx diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx new file mode 100644 index 000000000..bb49261a9 --- /dev/null +++ b/src/components/FixLargeThumbnail.tsx @@ -0,0 +1,108 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import constants from 'utils/strings/constants'; +import MessageDialog from './MessageDialog'; +import React, { useContext, useState } from 'react'; +import { ProgressBar, Button } from 'react-bootstrap'; +import { ComfySpan } from './ExportInProgress'; +import { regenerateThumbnail } from 'services/migrate'; +import { GalleryContext } from 'pages/gallery'; +import { RequestCanceller } from 'services/upload/queueProcessor'; +import { getLocalFiles } from 'services/fileService'; + +interface Props { + show: boolean; + hide: () => void; +} + +export default function FixLargeThumbnails(props: Props) { + const galleryContext = useContext(GalleryContext); + const [inprogress, SetInProgress] = useState(true); + const [updateCanceler, setUpdateCanceler] = useState({ + exec: () => null, + }); + const [progressTracker, setProgressTracker] = useState(); + const cancelFix = () => { + updateCanceler.exec(); + SetInProgress(false); + }; + const startFix = async () => { + SetInProgress(true); + const localFiles = await getLocalFiles(); + regenerateThumbnail(localFiles); + }; + return ( + +
+ {inprogress ? ( + <> +
+ + {' '} + {0} / {10}{' '} + {' '} + + {' '} + files exported + +
+
+ +
+ + ) : ( +
+ Your some files have of the thumbnails have been updated + of large size do you want to update them with low res + version to save space +
+ )} +
+ +
+ {inprogress ? ( + + ) : ( + + )} +
+
+ + ); +} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 8e1ac23e3..cf111826f 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -33,6 +33,7 @@ import exportService from 'services/exportService'; import { Subscription } from 'services/billingService'; import { PAGES } from 'types'; import { ARCHIVE_SECTION } from 'components/pages/gallery/Collections'; +import FixLargeThumbnails from './FixLargeThumbnail'; interface Props { collections: Collection[]; setDialogMessage: SetDialogMessage; @@ -50,6 +51,7 @@ export default function Sidebar(props: Props) { const [recoverModalView, setRecoveryModalView] = useState(false); const [twoFactorModalView, setTwoFactorModalView] = useState(false); const [exportModalView, setExportModalView] = useState(false); + const [fixLargeThumbsView, setFixLargeThumbsView] = useState(true); const galleryContext = useContext(GalleryContext); useEffect(() => { const main = async () => { @@ -267,6 +269,17 @@ export default function Sidebar(props: Props) { {constants.UPDATE_EMAIL} + <> + setFixLargeThumbsView(false)} + /> + setFixLargeThumbsView(true)}> + {constants.FIX_LARGE_THUMBNAILS} + + diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 2c849281a..d105b0c74 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -552,6 +552,7 @@ const englishConstants = { SORT_BY_LATEST_PHOTO: 'recent photo', SORT_BY_MODIFICATION_TIME: 'last updated', SORT_BY_COLLECTION_NAME: 'album name', + FIX_LARGE_THUMBNAILS: 'fix large thumbnails', }; export default englishConstants; From 6681b601a3374750045deee7d40d520e5feffc23 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 11:07:59 +0530 Subject: [PATCH 29/69] completes fix thumbnail modal UI --- src/components/FixLargeThumbnail.tsx | 123 ++++++++++++++----------- src/services/migrate/index.ts | 23 +++-- src/utils/strings/englishConstants.tsx | 16 ++++ 3 files changed, 98 insertions(+), 64 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index bb49261a9..55ef7f765 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -1,34 +1,51 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import constants from 'utils/strings/constants'; import MessageDialog from './MessageDialog'; -import React, { useContext, useState } from 'react'; +import React, { useState } from 'react'; import { ProgressBar, Button } from 'react-bootstrap'; import { ComfySpan } from './ExportInProgress'; import { regenerateThumbnail } from 'services/migrate'; -import { GalleryContext } from 'pages/gallery'; -import { RequestCanceller } from 'services/upload/queueProcessor'; -import { getLocalFiles } from 'services/fileService'; +export type SetProgressTracker = React.Dispatch< + React.SetStateAction<{ + current: number; + total: number; + }> +>; interface Props { show: boolean; hide: () => void; } - +export enum FIX_STATE { + NOT_STARTED, + RUNNING, + COMPLETED, + RAN_WITH_ERROR, +} +function Message(props: { fixState: FIX_STATE }) { + let message = <>; + switch (props.fixState) { + case FIX_STATE.NOT_STARTED: + message = constants.REPLACE_THUMBNAIL_NOT_STARTED(); + break; + case FIX_STATE.COMPLETED: + message = constants.REPLACE_THUMBNAIL_COMPLETED(); + break; + case FIX_STATE.RAN_WITH_ERROR: + message = constants.REPLACE_THUMBNAIL_RAN_WITH_ERROR(); + break; + } + return
{message}
; +} export default function FixLargeThumbnails(props: Props) { - const galleryContext = useContext(GalleryContext); - const [inprogress, SetInProgress] = useState(true); - const [updateCanceler, setUpdateCanceler] = useState({ - exec: () => null, + const [fixState, setFixState] = useState(FIX_STATE.NOT_STARTED); + const [progressTracker, setProgressTracker] = useState({ + current: 0, + total: 0, }); - const [progressTracker, setProgressTracker] = useState(); - const cancelFix = () => { - updateCanceler.exec(); - SetInProgress(false); - }; + const startFix = async () => { - SetInProgress(true); - const localFiles = await getLocalFiles(); - regenerateThumbnail(localFiles); + setFixState(FIX_STATE.RUNNING); + regenerateThumbnail(setProgressTracker); }; return ( - {inprogress ? ( + {fixState === FIX_STATE.RUNNING ? ( <>
{' '} - {0} / {10}{' '} + {progressTracker.current} /{' '} + {progressTracker.total}{' '} {' '} {' '} files exported
-
+
) : ( -
- Your some files have of the thumbnails have been updated - of large size do you want to update them with low res - version to save space -
+ <> + +
+ +
+ + +
+ )} -
- -
- {inprogress ? ( - - ) : ( - - )} -
); diff --git a/src/services/migrate/index.ts b/src/services/migrate/index.ts index fc74535d4..1aa7ab40e 100644 --- a/src/services/migrate/index.ts +++ b/src/services/migrate/index.ts @@ -1,5 +1,5 @@ import downloadManager from 'services/downloadManager'; -import { File, fileAttribute, FILE_TYPE } from 'services/fileService'; +import { fileAttribute, FILE_TYPE, getLocalFiles } from 'services/fileService'; import { generateThumbnail, MAX_THUMBNAIL_SIZE, @@ -9,9 +9,9 @@ import { logError } from 'utils/sentry'; import { getEndpoint } from 'utils/common/apiUtil'; import HTTPService from 'services/HTTPService'; import CryptoWorker from 'utils/crypto'; -import { convertToHumanReadable } from 'utils/billingUtil'; import uploadHttpClient from 'services/upload/uploadHttpClient'; import { EncryptionResult, UploadURL } from 'services/upload/uploadService'; +import { SetProgressTracker } from 'components/FixLargeThumbnail'; const ENDPOINT = getEndpoint(); @@ -36,22 +36,33 @@ export async function getLargeThumbnailFiles() { throw e; } } -export async function regenerateThumbnail(files: File[]) { +export async function regenerateThumbnail( + setProgressTracker: SetProgressTracker +) { try { const token = getToken(); const worker = await new CryptoWorker(); const largeThumbnailFileIDs = new Set( (await getLargeThumbnailFiles()) ?? [] ); + const files = await getLocalFiles(); const largeThumbnailFiles = files.filter((file) => largeThumbnailFileIDs.has(file.id) ); + if (largeThumbnailFiles.length === 0) { + return; + } + setProgressTracker({ current: 0, total: largeThumbnailFiles.length }); const uploadURLs: UploadURL[] = []; uploadHttpClient.fetchUploadURLs( largeThumbnailFiles.length, uploadURLs ); - for (const file of largeThumbnailFiles) { + for (const [idx, file] of largeThumbnailFiles.entries()) { + setProgressTracker({ + current: idx, + total: largeThumbnailFiles.length, + }); const originalThumbnail = await downloadManager.getThumbnail( token, file @@ -73,10 +84,6 @@ export async function regenerateThumbnail(files: File[]) { uploadURLs.pop() ); await updateThumbnail(file.id, newUploadedThumbnail); - console.log( - URL.createObjectURL(new Blob([newThumbnail])), - convertToHumanReadable(newThumbnail.length) - ); } } catch (e) { logError(e, 'failed to regenerate thumbnail'); diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index d105b0c74..c7ade0da5 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -553,6 +553,22 @@ const englishConstants = { SORT_BY_MODIFICATION_TIME: 'last updated', SORT_BY_COLLECTION_NAME: 'album name', FIX_LARGE_THUMBNAILS: 'fix large thumbnails', + REPLACE_THUMBNAIL_NOT_STARTED: () => ( + <> + Some of your files have been upload with very large thumbnail due to + bug in web client the thumbnails. do you want to update them with + low res version to save space + + ), + REPLACE_THUMBNAIL_COMPLETED: () => ( + <>your thumbnails have been successfully updated + ), + REPLACE_THUMBNAIL_RAN_WITH_ERROR: () => ( + <> + thumbnail update for some of your files failed ,please try to fix + them + + ), }; export default englishConstants; From 2789a14eeba000338fb3090d27089ff520f6844a Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 11:49:57 +0530 Subject: [PATCH 30/69] adds local state for fixLargeThumbnails --- src/components/FixLargeThumbnail.tsx | 71 ++++++++++++++++------------ src/services/migrate/index.ts | 67 ++++++++++++++------------ src/utils/storage/localStorage.ts | 1 + 3 files changed, 78 insertions(+), 61 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 55ef7f765..7f3e2f2d8 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -3,7 +3,8 @@ import MessageDialog from './MessageDialog'; import React, { useState } from 'react'; import { ProgressBar, Button } from 'react-bootstrap'; import { ComfySpan } from './ExportInProgress'; -import { regenerateThumbnail } from 'services/migrate'; +import { replaceThumbnail } from 'services/migrate'; +import { LS_KEYS, setData } from 'utils/storage/localStorage'; export type SetProgressTracker = React.Dispatch< React.SetStateAction<{ @@ -34,7 +35,7 @@ function Message(props: { fixState: FIX_STATE }) { message = constants.REPLACE_THUMBNAIL_RAN_WITH_ERROR(); break; } - return
{message}
; + return
{message}
; } export default function FixLargeThumbnails(props: Props) { const [fixState, setFixState] = useState(FIX_STATE.NOT_STARTED); @@ -44,8 +45,16 @@ export default function FixLargeThumbnails(props: Props) { }); const startFix = async () => { - setFixState(FIX_STATE.RUNNING); - regenerateThumbnail(setProgressTracker); + updateFixState(FIX_STATE.RUNNING); + const completedWithError = await replaceThumbnail(setProgressTracker); + updateFixState( + completedWithError ? FIX_STATE.RAN_WITH_ERROR : FIX_STATE.COMPLETED + ); + }; + + const updateFixState = (fixState: FIX_STATE) => { + setFixState(fixState); + setData(LS_KEYS.THUMBNAIL_FIX_STATE, { state: fixState }); }; return (
- {fixState === FIX_STATE.RUNNING ? ( + + + {fixState === FIX_STATE.RUNNING && ( <>
@@ -87,32 +98,30 @@ export default function FixLargeThumbnails(props: Props) { />
- ) : ( - <> - -
- -
- - -
- )} +
+ +
+ {(fixState === FIX_STATE.NOT_STARTED || + fixState === FIX_STATE.RAN_WITH_ERROR) && ( + + )} +
); diff --git a/src/services/migrate/index.ts b/src/services/migrate/index.ts index 1aa7ab40e..08afba1ed 100644 --- a/src/services/migrate/index.ts +++ b/src/services/migrate/index.ts @@ -36,9 +36,8 @@ export async function getLargeThumbnailFiles() { throw e; } } -export async function regenerateThumbnail( - setProgressTracker: SetProgressTracker -) { +export async function replaceThumbnail(setProgressTracker: SetProgressTracker) { + let completedWithError = false; try { const token = getToken(); const worker = await new CryptoWorker(); @@ -49,8 +48,9 @@ export async function regenerateThumbnail( const largeThumbnailFiles = files.filter((file) => largeThumbnailFileIDs.has(file.id) ); + if (largeThumbnailFiles.length === 0) { - return; + return completedWithError; } setProgressTracker({ current: 0, total: largeThumbnailFiles.length }); const uploadURLs: UploadURL[] = []; @@ -59,35 +59,42 @@ export async function regenerateThumbnail( uploadURLs ); for (const [idx, file] of largeThumbnailFiles.entries()) { - setProgressTracker({ - current: idx, - total: largeThumbnailFiles.length, - }); - const originalThumbnail = await downloadManager.getThumbnail( - token, - file - ); - const dummyImageFile = new globalThis.File( - [originalThumbnail], - file.metadata.title - ); - const { thumbnail: newThumbnail } = await generateThumbnail( - worker, - dummyImageFile, - FILE_TYPE.IMAGE, - false - ); - const newUploadedThumbnail = await uploadThumbnail( - worker, - file.key, - newThumbnail, - uploadURLs.pop() - ); - await updateThumbnail(file.id, newUploadedThumbnail); + try { + setProgressTracker({ + current: idx, + total: largeThumbnailFiles.length, + }); + const originalThumbnail = await downloadManager.getThumbnail( + token, + file + ); + const dummyImageFile = new globalThis.File( + [originalThumbnail], + file.metadata.title + ); + const { thumbnail: newThumbnail } = await generateThumbnail( + worker, + dummyImageFile, + FILE_TYPE.IMAGE, + false + ); + const newUploadedThumbnail = await uploadThumbnail( + worker, + file.key, + newThumbnail, + uploadURLs.pop() + ); + await updateThumbnail(file.id, newUploadedThumbnail); + } catch (e) { + logError(e, 'failed to replace a thumbnail'); + completedWithError = true; + } } } catch (e) { - logError(e, 'failed to regenerate thumbnail'); + logError(e, 'replace Thumbnail function failed'); + completedWithError = true; } + return completedWithError; } export async function uploadThumbnail( diff --git a/src/utils/storage/localStorage.ts b/src/utils/storage/localStorage.ts index 356bd6ab1..fddfe119e 100644 --- a/src/utils/storage/localStorage.ts +++ b/src/utils/storage/localStorage.ts @@ -12,6 +12,7 @@ export enum LS_KEYS { SHOW_BACK_BUTTON = 'showBackButton', EXPORT = 'export', AnonymizeUserID = 'anonymizedUserID', + THUMBNAIL_FIX_STATE = 'thumbnailFixState', } export const setData = (key: LS_KEYS, value: object) => { From 96e9e54112acc561faad17f95600babcdaaf42c8 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 12:03:18 +0530 Subject: [PATCH 31/69] testing large thumbnails --- src/services/upload/uploadService.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/services/upload/uploadService.ts b/src/services/upload/uploadService.ts index 156bd0d34..1663c30c2 100644 --- a/src/services/upload/uploadService.ts +++ b/src/services/upload/uploadService.ts @@ -7,7 +7,7 @@ import { getMetadataMapKey, ParsedMetaDataJSON, } from './metadataService'; -import { generateThumbnail } from './thumbnailService'; +// import { generateThumbnail } from './thumbnailService'; import { getFileOriginalName, getFileData, @@ -109,15 +109,17 @@ class UploadService { fileTypeInfo: FileTypeInfo ): Promise { const isHEIC = fileIsHEIC(fileTypeInfo.exactType); - - const { thumbnail, hasStaticThumbnail } = await generateThumbnail( - worker, - rawFile, - fileTypeInfo.fileType, - isHEIC - ); + console.log(isHEIC); + // const { thumbnail, hasStaticThumbnail } = await generateThumbnail( + // worker, + // rawFile, + // fileTypeInfo.fileType, + // isHEIC + // ); const filedata = await getFileData(worker, rawFile); + const hasStaticThumbnail = false; + const thumbnail = filedata as Uint8Array; return { filedata, From 4d0e1bacfa1bc22043aee887b2f1e58a8ff0b1c9 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 12:05:32 +0530 Subject: [PATCH 32/69] updated thumbnail fix UI --- src/components/FixLargeThumbnail.tsx | 32 +++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 7f3e2f2d8..c71c327f7 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -23,7 +23,7 @@ export enum FIX_STATE { RAN_WITH_ERROR, } function Message(props: { fixState: FIX_STATE }) { - let message = <>; + let message = null; switch (props.fixState) { case FIX_STATE.NOT_STARTED: message = constants.REPLACE_THUMBNAIL_NOT_STARTED(); @@ -35,7 +35,11 @@ function Message(props: { fixState: FIX_STATE }) { message = constants.REPLACE_THUMBNAIL_RAN_WITH_ERROR(); break; } - return
{message}
; + return message ? ( +
{message}
+ ) : ( + <> + ); } export default function FixLargeThumbnails(props: Props) { const [fixState, setFixState] = useState(FIX_STATE.NOT_STARTED); @@ -87,7 +91,12 @@ export default function FixLargeThumbnails(props: Props) { files exported
-
+
{constants.CLOSE} -
{(fixState === FIX_STATE.NOT_STARTED || fixState === FIX_STATE.RAN_WITH_ERROR) && ( - + <> +
+ + + )}
From fe70c1db590d98bf5ac4e23198fe61e1e5b70904 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 13:32:32 +0530 Subject: [PATCH 33/69] export strings to constants --- src/components/FixLargeThumbnail.tsx | 2 +- src/utils/strings/englishConstants.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index c71c327f7..261c1f17d 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -88,7 +88,7 @@ export default function FixLargeThumbnails(props: Props) { {' '} {' '} - files exported + {constants.THUMBNAIL_REPLACED}
( <> Some of your files have been upload with very large thumbnail due to From 1054380d095332aecd0b79b22cf0a9d5dfbce305 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 13:56:13 +0530 Subject: [PATCH 34/69] show fix thumbnail dialog if large thumbnail present on load --- src/components/FixLargeThumbnail.tsx | 34 +++++++++++++++++++++++----- src/components/Sidebar.tsx | 5 ++-- src/services/migrate/index.ts | 8 +++---- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 261c1f17d..805f3c938 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -1,10 +1,10 @@ import constants from 'utils/strings/constants'; import MessageDialog from './MessageDialog'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { ProgressBar, Button } from 'react-bootstrap'; import { ComfySpan } from './ExportInProgress'; -import { replaceThumbnail } from 'services/migrate'; -import { LS_KEYS, setData } from 'utils/storage/localStorage'; +import { getLargeThumbnailFiles, replaceThumbnail } from 'services/migrate'; +import { getData, LS_KEYS, setData } from 'utils/storage/localStorage'; export type SetProgressTracker = React.Dispatch< React.SetStateAction<{ @@ -13,7 +13,8 @@ export type SetProgressTracker = React.Dispatch< }> >; interface Props { - show: boolean; + isOpen: boolean; + show: () => void; hide: () => void; } export enum FIX_STATE { @@ -47,10 +48,31 @@ export default function FixLargeThumbnails(props: Props) { current: 0, total: 0, }); + const [largeThumbnailFiles, setLargeThumbnailFiles] = useState( + [] + ); + useEffect(() => { + const main = async () => { + const largeThumbnailFiles = await getLargeThumbnailFiles(); + if (largeThumbnailFiles?.length > 0) { + props.show(); + } + setLargeThumbnailFiles(largeThumbnailFiles); + }; + if (props.isOpen) { + const fixState = getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state; + console.log(fixState); + setFixState(fixState ?? FIX_STATE.NOT_STARTED); + main(); + } + }, [props.isOpen]); const startFix = async () => { updateFixState(FIX_STATE.RUNNING); - const completedWithError = await replaceThumbnail(setProgressTracker); + const completedWithError = await replaceThumbnail( + setProgressTracker, + new Set(largeThumbnailFiles) + ); updateFixState( completedWithError ? FIX_STATE.RAN_WITH_ERROR : FIX_STATE.COMPLETED ); @@ -62,7 +84,7 @@ export default function FixLargeThumbnails(props: Props) { }; return ( { const main = async () => { @@ -271,8 +271,9 @@ export default function Sidebar(props: Props) { <> setFixLargeThumbsView(false)} + show={() => setFixLargeThumbsView(true)} /> +) { let completedWithError = false; try { const token = getToken(); const worker = await new CryptoWorker(); - const largeThumbnailFileIDs = new Set( - (await getLargeThumbnailFiles()) ?? [] - ); const files = await getLocalFiles(); const largeThumbnailFiles = files.filter((file) => largeThumbnailFileIDs.has(file.id) From 2f03c678f852e042cf7fed9cedad8ad43f7c80a4 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 15:33:21 +0530 Subject: [PATCH 35/69] adds completed but has more state to fix large thumbnail --- src/components/FixLargeThumbnail.tsx | 35 ++++++++++++++++++-------- src/utils/strings/englishConstants.tsx | 11 ++++++-- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 805f3c938..2e0ecdacc 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -21,7 +21,8 @@ export enum FIX_STATE { NOT_STARTED, RUNNING, COMPLETED, - RAN_WITH_ERROR, + COMPLETED_WITH_ERRORS, + COMPLETED_BUT_HAS_MORE, } function Message(props: { fixState: FIX_STATE }) { let message = null; @@ -32,8 +33,11 @@ function Message(props: { fixState: FIX_STATE }) { case FIX_STATE.COMPLETED: message = constants.REPLACE_THUMBNAIL_COMPLETED(); break; - case FIX_STATE.RAN_WITH_ERROR: - message = constants.REPLACE_THUMBNAIL_RAN_WITH_ERROR(); + case FIX_STATE.COMPLETED_WITH_ERRORS: + message = constants.REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR(); + break; + case FIX_STATE.COMPLETED_BUT_HAS_MORE: + message = constants.REPLACE_THUMBNAIL_COMPLETED_BUT_HAS_MORE(); break; } return message ? ( @@ -56,14 +60,20 @@ export default function FixLargeThumbnails(props: Props) { const main = async () => { const largeThumbnailFiles = await getLargeThumbnailFiles(); if (largeThumbnailFiles?.length > 0) { - props.show(); + setLargeThumbnailFiles(largeThumbnailFiles); } - setLargeThumbnailFiles(largeThumbnailFiles); + let fixState = + getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state ?? + FIX_STATE.NOT_STARTED; + if ( + fixState === FIX_STATE.COMPLETED && + largeThumbnailFiles.length > 0 + ) { + fixState = FIX_STATE.COMPLETED_BUT_HAS_MORE; + } + setFixState(fixState); }; if (props.isOpen) { - const fixState = getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state; - console.log(fixState); - setFixState(fixState ?? FIX_STATE.NOT_STARTED); main(); } }, [props.isOpen]); @@ -74,7 +84,9 @@ export default function FixLargeThumbnails(props: Props) { new Set(largeThumbnailFiles) ); updateFixState( - completedWithError ? FIX_STATE.RAN_WITH_ERROR : FIX_STATE.COMPLETED + completedWithError + ? FIX_STATE.COMPLETED_WITH_ERRORS + : FIX_STATE.COMPLETED ); }; @@ -143,7 +155,8 @@ export default function FixLargeThumbnails(props: Props) { {constants.CLOSE} {(fixState === FIX_STATE.NOT_STARTED || - fixState === FIX_STATE.RAN_WITH_ERROR) && ( + fixState === FIX_STATE.COMPLETED_WITH_ERRORS || + fixState === FIX_STATE.COMPLETED_BUT_HAS_MORE) && ( <>
@@ -151,7 +164,7 @@ export default function FixLargeThumbnails(props: Props) { block variant={'outline-success'} onClick={startFix}> - {constants.UPDATE} + {constants.FIX} )} diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index a9d9657d9..c329659a2 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -554,6 +554,7 @@ const englishConstants = { SORT_BY_COLLECTION_NAME: 'album name', FIX_LARGE_THUMBNAILS: 'fix large thumbnails', THUMBNAIL_REPLACED: 'thumbnail replaced', + FIX: 'fix', REPLACE_THUMBNAIL_NOT_STARTED: () => ( <> Some of your files have been upload with very large thumbnail due to @@ -564,9 +565,15 @@ const englishConstants = { REPLACE_THUMBNAIL_COMPLETED: () => ( <>your thumbnails have been successfully updated ), - REPLACE_THUMBNAIL_RAN_WITH_ERROR: () => ( + REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR: () => ( <> - thumbnail update for some of your files failed ,please try to fix + thumbnail update for some of your files failed, please run again to + fix them + + ), + REPLACE_THUMBNAIL_COMPLETED_BUT_HAS_MORE: () => ( + <> + thumbnail update pending for some files, please run again to fix them ), From 45ecc1cb1c2c32423357eca8cd809a0d3b52c26e Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 15:59:34 +0530 Subject: [PATCH 36/69] auto run thumbnail if started in previous load --- src/components/FixLargeThumbnail.tsx | 91 ++++++++++++++++++---------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 2e0ecdacc..9acad9c72 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -19,6 +19,7 @@ interface Props { } export enum FIX_STATE { NOT_STARTED, + FIX_LATER, RUNNING, COMPLETED, COMPLETED_WITH_ERRORS, @@ -28,6 +29,7 @@ function Message(props: { fixState: FIX_STATE }) { let message = null; switch (props.fixState) { case FIX_STATE.NOT_STARTED: + case FIX_STATE.FIX_LATER: message = constants.REPLACE_THUMBNAIL_NOT_STARTED(); break; case FIX_STATE.COMPLETED: @@ -56,38 +58,52 @@ export default function FixLargeThumbnails(props: Props) { [] ); + const main = async () => { + const largeThumbnailFiles = await getLargeThumbnailFiles(); + if (largeThumbnailFiles?.length > 0) { + setLargeThumbnailFiles(largeThumbnailFiles); + } + let fixState = + getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state ?? + FIX_STATE.NOT_STARTED; + if ( + fixState === FIX_STATE.COMPLETED && + largeThumbnailFiles.length > 0 + ) { + fixState = FIX_STATE.COMPLETED_BUT_HAS_MORE; + } + setFixState(fixState); + if (fixState === FIX_STATE.RUNNING) { + startFix(largeThumbnailFiles); + } + if (fixState === FIX_STATE.NOT_STARTED) { + props.show(); + } + }; useEffect(() => { - const main = async () => { - const largeThumbnailFiles = await getLargeThumbnailFiles(); - if (largeThumbnailFiles?.length > 0) { - setLargeThumbnailFiles(largeThumbnailFiles); - } - let fixState = - getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state ?? - FIX_STATE.NOT_STARTED; - if ( - fixState === FIX_STATE.COMPLETED && - largeThumbnailFiles.length > 0 - ) { - fixState = FIX_STATE.COMPLETED_BUT_HAS_MORE; - } - setFixState(fixState); - }; - if (props.isOpen) { + if (props.isOpen && fixState !== FIX_STATE.RUNNING) { main(); } }, [props.isOpen]); - const startFix = async () => { + + useEffect(() => { + main(); + }, []); + const startFix = async (newlyFetchedLargeThumbnailFiles?: number[]) => { updateFixState(FIX_STATE.RUNNING); const completedWithError = await replaceThumbnail( setProgressTracker, - new Set(largeThumbnailFiles) - ); - updateFixState( - completedWithError - ? FIX_STATE.COMPLETED_WITH_ERRORS - : FIX_STATE.COMPLETED + new Set( + newlyFetchedLargeThumbnailFiles ?? largeThumbnailFiles ?? [] + ) ); + if (typeof completedWithError !== 'undefined') { + updateFixState( + completedWithError + ? FIX_STATE.COMPLETED_WITH_ERRORS + : FIX_STATE.COMPLETED + ); + } }; const updateFixState = (fixState: FIX_STATE) => { @@ -148,13 +164,26 @@ export default function FixLargeThumbnails(props: Props) { display: 'flex', justifyContent: 'space-around', }}> - + {fixState === FIX_STATE.NOT_STARTED ? ( + + ) : ( + + )} {(fixState === FIX_STATE.NOT_STARTED || + fixState === FIX_STATE.FIX_LATER || fixState === FIX_STATE.COMPLETED_WITH_ERRORS || fixState === FIX_STATE.COMPLETED_BUT_HAS_MORE) && ( <> @@ -163,7 +192,7 @@ export default function FixLargeThumbnails(props: Props) { From 295b1d6ac278a33cea8ba73a20b1779d9fd33fe1 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 15:59:46 +0530 Subject: [PATCH 37/69] new string added --- src/utils/strings/englishConstants.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index c329659a2..3dbf30878 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -555,6 +555,7 @@ const englishConstants = { FIX_LARGE_THUMBNAILS: 'fix large thumbnails', THUMBNAIL_REPLACED: 'thumbnail replaced', FIX: 'fix', + FIX_LATER: 'fix later', REPLACE_THUMBNAIL_NOT_STARTED: () => ( <> Some of your files have been upload with very large thumbnail due to From 1aa93b29d876aa542c83eca652e70963a006d244 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 16:15:23 +0530 Subject: [PATCH 38/69] setLargeThumbnailFiles with getLargeThumbnailFiles always --- src/components/FixLargeThumbnail.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 9acad9c72..2ef707a1c 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -60,9 +60,7 @@ export default function FixLargeThumbnails(props: Props) { const main = async () => { const largeThumbnailFiles = await getLargeThumbnailFiles(); - if (largeThumbnailFiles?.length > 0) { - setLargeThumbnailFiles(largeThumbnailFiles); - } + setLargeThumbnailFiles(largeThumbnailFiles ?? []); let fixState = getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state ?? FIX_STATE.NOT_STARTED; From bc561f804b2438c44144789a0e79900a500011c0 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 16:18:40 +0530 Subject: [PATCH 39/69] Revert "testing large thumbnails" This reverts commit 96e9e54112acc561faad17f95600babcdaaf42c8. --- src/services/upload/uploadService.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/services/upload/uploadService.ts b/src/services/upload/uploadService.ts index 1663c30c2..156bd0d34 100644 --- a/src/services/upload/uploadService.ts +++ b/src/services/upload/uploadService.ts @@ -7,7 +7,7 @@ import { getMetadataMapKey, ParsedMetaDataJSON, } from './metadataService'; -// import { generateThumbnail } from './thumbnailService'; +import { generateThumbnail } from './thumbnailService'; import { getFileOriginalName, getFileData, @@ -109,17 +109,15 @@ class UploadService { fileTypeInfo: FileTypeInfo ): Promise { const isHEIC = fileIsHEIC(fileTypeInfo.exactType); - console.log(isHEIC); - // const { thumbnail, hasStaticThumbnail } = await generateThumbnail( - // worker, - // rawFile, - // fileTypeInfo.fileType, - // isHEIC - // ); + + const { thumbnail, hasStaticThumbnail } = await generateThumbnail( + worker, + rawFile, + fileTypeInfo.fileType, + isHEIC + ); const filedata = await getFileData(worker, rawFile); - const hasStaticThumbnail = false; - const thumbnail = filedata as Uint8Array; return { filedata, From c9959994a5380de860b190724e4cd93b5aa9fff7 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 16:25:29 +0530 Subject: [PATCH 40/69] rename migrate to migrateThumbnailService --- src/components/FixLargeThumbnail.tsx | 5 ++++- .../{migrate/index.ts => migrateThumbnailService.ts} | 0 2 files changed, 4 insertions(+), 1 deletion(-) rename src/services/{migrate/index.ts => migrateThumbnailService.ts} (100%) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 2ef707a1c..998abc012 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -3,7 +3,10 @@ import MessageDialog from './MessageDialog'; import React, { useEffect, useState } from 'react'; import { ProgressBar, Button } from 'react-bootstrap'; import { ComfySpan } from './ExportInProgress'; -import { getLargeThumbnailFiles, replaceThumbnail } from 'services/migrate'; +import { + getLargeThumbnailFiles, + replaceThumbnail, +} from 'services/migrateThumbnailService'; import { getData, LS_KEYS, setData } from 'utils/storage/localStorage'; export type SetProgressTracker = React.Dispatch< diff --git a/src/services/migrate/index.ts b/src/services/migrateThumbnailService.ts similarity index 100% rename from src/services/migrate/index.ts rename to src/services/migrateThumbnailService.ts From c3a5d731953a7a5f1eda244f5015712f0957af8a Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 18:01:01 +0530 Subject: [PATCH 41/69] update replace thumbnails strings --- src/utils/strings/englishConstants.tsx | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 3dbf30878..1f920558a 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -552,31 +552,21 @@ const englishConstants = { SORT_BY_LATEST_PHOTO: 'recent photo', SORT_BY_MODIFICATION_TIME: 'last updated', SORT_BY_COLLECTION_NAME: 'album name', - FIX_LARGE_THUMBNAILS: 'fix large thumbnails', - THUMBNAIL_REPLACED: 'thumbnail replaced', + FIX_LARGE_THUMBNAILS: 'compress thumbnails', + THUMBNAIL_REPLACED: 'thumbnails compressed', FIX: 'fix', FIX_LATER: 'fix later', REPLACE_THUMBNAIL_NOT_STARTED: () => ( <> - Some of your files have been upload with very large thumbnail due to - bug in web client the thumbnails. do you want to update them with - low res version to save space + some of your videos thumbnails can be compressed to save space. + would you like ente to compress them? ), REPLACE_THUMBNAIL_COMPLETED: () => ( - <>your thumbnails have been successfully updated + <>successfully compressed all thumbnails ), REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR: () => ( - <> - thumbnail update for some of your files failed, please run again to - fix them - - ), - REPLACE_THUMBNAIL_COMPLETED_BUT_HAS_MORE: () => ( - <> - thumbnail update pending for some files, please run again to fix - them - + <>could not compress some of your thumbnails, please retry ), }; From 48033338816d07e05f7f8ab6a04fa5b6c4f23fc0 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 18:06:03 +0530 Subject: [PATCH 42/69] remove completed but hasMore state --- src/components/FixLargeThumbnail.tsx | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 998abc012..9bfdc4be3 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -26,7 +26,6 @@ export enum FIX_STATE { RUNNING, COMPLETED, COMPLETED_WITH_ERRORS, - COMPLETED_BUT_HAS_MORE, } function Message(props: { fixState: FIX_STATE }) { let message = null; @@ -41,9 +40,6 @@ function Message(props: { fixState: FIX_STATE }) { case FIX_STATE.COMPLETED_WITH_ERRORS: message = constants.REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR(); break; - case FIX_STATE.COMPLETED_BUT_HAS_MORE: - message = constants.REPLACE_THUMBNAIL_COMPLETED_BUT_HAS_MORE(); - break; } return message ? (
{message}
@@ -64,15 +60,9 @@ export default function FixLargeThumbnails(props: Props) { const main = async () => { const largeThumbnailFiles = await getLargeThumbnailFiles(); setLargeThumbnailFiles(largeThumbnailFiles ?? []); - let fixState = + const fixState = getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state ?? FIX_STATE.NOT_STARTED; - if ( - fixState === FIX_STATE.COMPLETED && - largeThumbnailFiles.length > 0 - ) { - fixState = FIX_STATE.COMPLETED_BUT_HAS_MORE; - } setFixState(fixState); if (fixState === FIX_STATE.RUNNING) { startFix(largeThumbnailFiles); @@ -185,8 +175,7 @@ export default function FixLargeThumbnails(props: Props) { )} {(fixState === FIX_STATE.NOT_STARTED || fixState === FIX_STATE.FIX_LATER || - fixState === FIX_STATE.COMPLETED_WITH_ERRORS || - fixState === FIX_STATE.COMPLETED_BUT_HAS_MORE) && ( + fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && ( <>
From 4dbbc0fe51dc22b0144fa3b659e4e6b2848864a4 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Mon, 18 Oct 2021 18:12:44 +0530 Subject: [PATCH 43/69] updated strings --- src/utils/strings/englishConstants.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 1f920558a..c1fae4b37 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -554,8 +554,8 @@ const englishConstants = { SORT_BY_COLLECTION_NAME: 'album name', FIX_LARGE_THUMBNAILS: 'compress thumbnails', THUMBNAIL_REPLACED: 'thumbnails compressed', - FIX: 'fix', - FIX_LATER: 'fix later', + FIX: 'compress', + FIX_LATER: 'compress later', REPLACE_THUMBNAIL_NOT_STARTED: () => ( <> some of your videos thumbnails can be compressed to save space. From b53c978093ac941d347e88d9d2cb4abbd85c0e62 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 19 Oct 2021 13:49:03 +0530 Subject: [PATCH 44/69] set REPLACE_THUMBNAIL_THRESHOLD to 500KB --- src/services/migrateThumbnailService.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/services/migrateThumbnailService.ts b/src/services/migrateThumbnailService.ts index 560374c33..672c7bb17 100644 --- a/src/services/migrateThumbnailService.ts +++ b/src/services/migrateThumbnailService.ts @@ -1,9 +1,6 @@ import downloadManager from 'services/downloadManager'; import { fileAttribute, FILE_TYPE, getLocalFiles } from 'services/fileService'; -import { - generateThumbnail, - MAX_THUMBNAIL_SIZE, -} from 'services/upload/thumbnailService'; +import { generateThumbnail } from 'services/upload/thumbnailService'; import { getToken } from 'utils/common/key'; import { logError } from 'utils/sentry'; import { getEndpoint } from 'utils/common/apiUtil'; @@ -14,7 +11,7 @@ import { EncryptionResult, UploadURL } from 'services/upload/uploadService'; import { SetProgressTracker } from 'components/FixLargeThumbnail'; const ENDPOINT = getEndpoint(); - +const REPLACE_THUMBNAIL_THRESHOLD = 500 * 1024; // 500KB export async function getLargeThumbnailFiles() { try { const token = getToken(); @@ -24,7 +21,7 @@ export async function getLargeThumbnailFiles() { const resp = await HTTPService.get( `${ENDPOINT}/files/large-thumbnails`, { - threshold: 2 * MAX_THUMBNAIL_SIZE, + threshold: REPLACE_THUMBNAIL_THRESHOLD, }, { 'X-Auth-Token': token, From 78a69a6d33e16cd6882c3b5bb9613353d50176af Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 19 Oct 2021 13:58:10 +0530 Subject: [PATCH 45/69] if compression more than min compression diff keep on compressing --- src/services/upload/thumbnailService.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 434de1461..6da775027 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -5,8 +5,8 @@ import { BLACK_THUMBNAIL_BASE64 } from '../../../public/images/black-thumbnail-b import FFmpegService from 'services/ffmpegService'; const THUMBNAIL_HEIGHT = 720; -const MAX_ATTEMPTS = 3; -const MIN_THUMBNAIL_SIZE = 50000; +const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10; +export const MAX_THUMBNAIL_SIZE = 50 * 1024; const WAIT_TIME_THUMBNAIL_GENERATION = 10 * 1000; @@ -171,11 +171,14 @@ export async function generateVideoThumbnail(file: globalThis.File) { } async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { - let thumbnailBlob = null; - let attempts = 0; + let thumbnailBlob: Blob = null; + let prevSize = Number.MAX_SAFE_INTEGER; let quality = 1; do { + if (thumbnailBlob) { + prevSize = thumbnailBlob.size; + } thumbnailBlob = await new Promise((resolve) => { canvas.toBlob( function (blob) { @@ -186,12 +189,19 @@ async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { ); }); thumbnailBlob = thumbnailBlob ?? new Blob([]); - attempts++; quality /= 2; } while ( - thumbnailBlob.size > MIN_THUMBNAIL_SIZE && - attempts <= MAX_ATTEMPTS + thumbnailBlob.size > MAX_THUMBNAIL_SIZE && + percentageSizeDiff(thumbnailBlob.size, prevSize) >= + MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF ); return thumbnailBlob; } + +function percentageSizeDiff( + newThumbnailSize: number, + oldThumbnailSize: number +) { + return ((oldThumbnailSize - newThumbnailSize) * 100) / oldThumbnailSize; +} From 175e323c4742ed5da56ada49f65d83ab2c82767a Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 19 Oct 2021 14:02:11 +0530 Subject: [PATCH 46/69] log thumbnail size greater than max limit --- src/services/upload/thumbnailService.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 6da775027..8b41c739e 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -195,6 +195,12 @@ async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { percentageSizeDiff(thumbnailBlob.size, prevSize) >= MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF ); + if (thumbnailBlob.size > MAX_THUMBNAIL_SIZE) { + logError( + Error('thumbnail_too_large'), + 'thumbnail greater than max limit' + ); + } return thumbnailBlob; } From 47914f5281b6685cff065e32234104a05969804a Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 19 Oct 2021 15:46:15 +0530 Subject: [PATCH 47/69] reduce quality linearly --- src/services/upload/thumbnailService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 8b41c739e..39fd9449d 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -189,8 +189,9 @@ async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { ); }); thumbnailBlob = thumbnailBlob ?? new Blob([]); - quality /= 2; + quality -= 0.1; } while ( + quality > 0 && thumbnailBlob.size > MAX_THUMBNAIL_SIZE && percentageSizeDiff(thumbnailBlob.size, prevSize) >= MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF From 1373a955aab14a1b5a5e80e2ce6edfa3a212214a Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 19 Oct 2021 17:11:34 +0530 Subject: [PATCH 48/69] make downloadThumb private correctly --- src/services/downloadManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index af2c72e92..a4eb2545c 100644 --- a/src/services/downloadManager.ts +++ b/src/services/downloadManager.ts @@ -24,7 +24,7 @@ class DownloadManager { return URL.createObjectURL(await cacheResp.blob()); } if (!this.thumbnailObjectUrlPromise.get(file.id)) { - const downloadPromise = this._downloadThumb( + const downloadPromise = this.downloadThumb( token, thumbnailCache, file @@ -38,7 +38,7 @@ class DownloadManager { } } - _downloadThumb = async ( + private downloadThumb = async ( token: string, thumbnailCache: Cache, file: File From d3b73eaa6bf4393cabd51444bf3ce6778c97a0bd Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 19 Oct 2021 17:12:56 +0530 Subject: [PATCH 49/69] fix file not retrying download after a fail --- src/services/downloadManager.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index a4eb2545c..577cd8037 100644 --- a/src/services/downloadManager.ts +++ b/src/services/downloadManager.ts @@ -67,6 +67,7 @@ class DownloadManager { }; getFile = async (file: File, forPreview = false) => { + const fileUID = `${file.id}_${forPreview}`; try { const getFilePromise = (async () => { const fileStream = await this.downloadFile(file); @@ -76,16 +77,12 @@ class DownloadManager { } return URL.createObjectURL(fileBlob); })(); - if (!this.fileObjectUrlPromise.get(`${file.id}_${forPreview}`)) { - this.fileObjectUrlPromise.set( - `${file.id}_${forPreview}`, - getFilePromise - ); + if (!this.fileObjectUrlPromise.get(fileUID)) { + this.fileObjectUrlPromise.set(fileUID, getFilePromise); } - return await this.fileObjectUrlPromise.get( - `${file.id}_${forPreview}` - ); + return await this.fileObjectUrlPromise.get(fileUID); } catch (e) { + this.fileObjectUrlPromise.delete(fileUID); logError(e, 'Failed to get File'); } }; From 89707411583ae26adc13faaa6726514513c14996 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Tue, 19 Oct 2021 17:59:31 +0530 Subject: [PATCH 50/69] fix file download even if present in local bug --- src/services/downloadManager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index 577cd8037..380841e7d 100644 --- a/src/services/downloadManager.ts +++ b/src/services/downloadManager.ts @@ -69,16 +69,16 @@ class DownloadManager { getFile = async (file: File, forPreview = false) => { const fileUID = `${file.id}_${forPreview}`; try { - const getFilePromise = (async () => { + const getFilePromise = async () => { const fileStream = await this.downloadFile(file); let fileBlob = await new Response(fileStream).blob(); if (forPreview) { fileBlob = await convertForPreview(file, fileBlob); } return URL.createObjectURL(fileBlob); - })(); + }; if (!this.fileObjectUrlPromise.get(fileUID)) { - this.fileObjectUrlPromise.set(fileUID, getFilePromise); + this.fileObjectUrlPromise.set(fileUID, getFilePromise()); } return await this.fileObjectUrlPromise.get(fileUID); } catch (e) { From 6097e76fad89d5f9701493fa87b6501df8174c82 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 21 Oct 2021 13:47:11 +0530 Subject: [PATCH 51/69] fixed thumbnail and file download not retried after fail --- src/components/PhotoFrame.tsx | 76 +++++++++++--------- src/components/pages/gallery/PreviewCard.tsx | 20 +++--- src/services/downloadManager.ts | 2 + 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index 40bd25788..ed087b9a9 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -301,46 +301,56 @@ const PhotoFrame = ({ const getSlideData = async (instance: any, index: number, item: File) => { if (!item.msrc) { - let url: string; - if (galleryContext.thumbs.has(item.id)) { - url = galleryContext.thumbs.get(item.id); - } else { - url = await DownloadManager.getPreview(item); - galleryContext.thumbs.set(item.id, url); - } - updateUrl(item.dataIndex)(url); - item.msrc = url; - if (!item.src) { - item.src = url; - } - item.w = window.innerWidth; - item.h = window.innerHeight; try { - instance.invalidateCurrItems(); - instance.updateSize(true); + let url: string; + if (galleryContext.thumbs.has(item.id)) { + url = galleryContext.thumbs.get(item.id); + } else { + url = await DownloadManager.getPreview(item); + galleryContext.thumbs.set(item.id, url); + } + updateUrl(item.dataIndex)(url); + item.msrc = url; + if (!item.src) { + item.src = url; + } + item.w = window.innerWidth; + item.h = window.innerHeight; + try { + instance.invalidateCurrItems(); + instance.updateSize(true); + } catch (e) { + // ignore + } } catch (e) { - // ignore + // no-op } } if (!fetching[item.dataIndex]) { - fetching[item.dataIndex] = true; - let url: string; - if (galleryContext.files.has(item.id)) { - url = galleryContext.files.get(item.id); - } else { - url = await DownloadManager.getFile(item, true); - galleryContext.files.set(item.id, url); - } - await updateSrcUrl(item.dataIndex, url); - item.html = files[item.dataIndex].html; - item.src = files[item.dataIndex].src; - item.w = files[item.dataIndex].w; - item.h = files[item.dataIndex].h; try { - instance.invalidateCurrItems(); - instance.updateSize(true); + fetching[item.dataIndex] = true; + let url: string; + if (galleryContext.files.has(item.id)) { + url = galleryContext.files.get(item.id); + } else { + url = await DownloadManager.getFile(item, true); + galleryContext.files.set(item.id, url); + } + await updateSrcUrl(item.dataIndex, url); + item.html = files[item.dataIndex].html; + item.src = files[item.dataIndex].src; + item.w = files[item.dataIndex].w; + item.h = files[item.dataIndex].h; + try { + instance.invalidateCurrItems(); + instance.updateSize(true); + } catch (e) { + // ignore + } } catch (e) { - // ignore + // no-op + } finally { + fetching[item.dataIndex] = false; } } }; diff --git a/src/components/pages/gallery/PreviewCard.tsx b/src/components/pages/gallery/PreviewCard.tsx index 5c698ffb0..bacbff199 100644 --- a/src/components/pages/gallery/PreviewCard.tsx +++ b/src/components/pages/gallery/PreviewCard.tsx @@ -128,15 +128,19 @@ export default function PreviewCard(props: IProps) { useLayoutEffect(() => { if (file && !file.msrc) { const main = async () => { - const url = await DownloadManager.getPreview(file); - if (isMounted.current) { - setImgSrc(url); - thumbs.set(file.id, url); - file.msrc = url; - if (!file.src) { - file.src = url; + try { + const url = await DownloadManager.getPreview(file); + if (isMounted.current) { + setImgSrc(url); + thumbs.set(file.id, url); + file.msrc = url; + if (!file.src) { + file.src = url; + } + updateUrl(url); } - updateUrl(url); + } catch (e) { + // no-op } }; diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index 380841e7d..40448fd51 100644 --- a/src/services/downloadManager.ts +++ b/src/services/downloadManager.ts @@ -35,6 +35,7 @@ class DownloadManager { } catch (e) { this.thumbnailObjectUrlPromise.delete(file.id); logError(e, 'get preview Failed'); + throw e; } } @@ -84,6 +85,7 @@ class DownloadManager { } catch (e) { this.fileObjectUrlPromise.delete(fileUID); logError(e, 'Failed to get File'); + throw e; } }; From 18b27f5f088c111ef0eb70cc14dde19cc4e44af4 Mon Sep 17 00:00:00 2001 From: abhinav-grd Date: Thu, 21 Oct 2021 16:12:00 +0530 Subject: [PATCH 52/69] dont have seperate preview file entry for video --- src/services/downloadManager.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index 40448fd51..a0545ea85 100644 --- a/src/services/downloadManager.ts +++ b/src/services/downloadManager.ts @@ -68,7 +68,12 @@ class DownloadManager { }; getFile = async (file: File, forPreview = false) => { - const fileUID = `${file.id}_${forPreview}`; + let fileUID: string; + if (file.metadata.fileType === FILE_TYPE.VIDEO) { + fileUID = file.id.toString(); + } else { + fileUID = `${file.id}_forPreview=${forPreview}`; + } try { const getFilePromise = async () => { const fileStream = await this.downloadFile(file); From dac5ea727ad03296f8073ab3e09b31e397cace91 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 25 Oct 2021 20:07:51 +0530 Subject: [PATCH 53/69] updated min and max compression quality and MAX_THUMBNAIL_SIZE --- src/services/upload/thumbnailService.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 39fd9449d..0e2949343 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -6,7 +6,9 @@ import FFmpegService from 'services/ffmpegService'; const THUMBNAIL_HEIGHT = 720; const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10; -export const MAX_THUMBNAIL_SIZE = 50 * 1024; +export const MAX_THUMBNAIL_SIZE = 100 * 1024; +const MIN_COMPRESSION_QUALITY = 0.7; +const MAX_COMPRESSION_QUALITY = 0.1; const WAIT_TIME_THUMBNAIL_GENERATION = 10 * 1000; @@ -173,7 +175,7 @@ export async function generateVideoThumbnail(file: globalThis.File) { async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { let thumbnailBlob: Blob = null; let prevSize = Number.MAX_SAFE_INTEGER; - let quality = 1; + let quality = MIN_COMPRESSION_QUALITY; do { if (thumbnailBlob) { @@ -191,7 +193,7 @@ async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { thumbnailBlob = thumbnailBlob ?? new Blob([]); quality -= 0.1; } while ( - quality > 0 && + quality > MAX_COMPRESSION_QUALITY && thumbnailBlob.size > MAX_THUMBNAIL_SIZE && percentageSizeDiff(thumbnailBlob.size, prevSize) >= MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF From b14855e7abc90cff505e6746bba91bdecf7c8c18 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 26 Oct 2021 10:43:14 +0530 Subject: [PATCH 54/69] only getThumbnail at start if migration not started --- src/components/FixLargeThumbnail.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 9bfdc4be3..3c342856e 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -57,13 +57,19 @@ export default function FixLargeThumbnails(props: Props) { [] ); + const init = (): FIX_STATE => { + let fixState = getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state; + if (!fixState) { + fixState = FIX_STATE.NOT_STARTED; + updateFixState(fixState); + } + setFixState(fixState); + return fixState; + }; + const main = async () => { const largeThumbnailFiles = await getLargeThumbnailFiles(); setLargeThumbnailFiles(largeThumbnailFiles ?? []); - const fixState = - getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state ?? - FIX_STATE.NOT_STARTED; - setFixState(fixState); if (fixState === FIX_STATE.RUNNING) { startFix(largeThumbnailFiles); } @@ -78,7 +84,10 @@ export default function FixLargeThumbnails(props: Props) { }, [props.isOpen]); useEffect(() => { - main(); + const fixState = init(); + if (fixState === FIX_STATE.NOT_STARTED) { + main(); + } }, []); const startFix = async (newlyFetchedLargeThumbnailFiles?: number[]) => { updateFixState(FIX_STATE.RUNNING); From 69067d89da420d79a26cf70500ecfacef9092679 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 26 Oct 2021 10:44:26 +0530 Subject: [PATCH 55/69] dont autoStart thumbnail fix --- src/components/FixLargeThumbnail.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 3c342856e..06a74e19c 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -59,7 +59,7 @@ export default function FixLargeThumbnails(props: Props) { const init = (): FIX_STATE => { let fixState = getData(LS_KEYS.THUMBNAIL_FIX_STATE)?.state; - if (!fixState) { + if (!fixState || fixState === FIX_STATE.RUNNING) { fixState = FIX_STATE.NOT_STARTED; updateFixState(fixState); } @@ -70,9 +70,6 @@ export default function FixLargeThumbnails(props: Props) { const main = async () => { const largeThumbnailFiles = await getLargeThumbnailFiles(); setLargeThumbnailFiles(largeThumbnailFiles ?? []); - if (fixState === FIX_STATE.RUNNING) { - startFix(largeThumbnailFiles); - } if (fixState === FIX_STATE.NOT_STARTED) { props.show(); } From 565f871a3d40a53e689010d604545a9b5f57f6b9 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 26 Oct 2021 10:45:19 +0530 Subject: [PATCH 56/69] logError if largeThumbnail left after migration complete --- src/components/FixLargeThumbnail.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 06a74e19c..3db85a03a 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -8,6 +8,7 @@ import { replaceThumbnail, } from 'services/migrateThumbnailService'; import { getData, LS_KEYS, setData } from 'utils/storage/localStorage'; +import { logError } from 'utils/sentry'; export type SetProgressTracker = React.Dispatch< React.SetStateAction<{ @@ -73,6 +74,13 @@ export default function FixLargeThumbnails(props: Props) { if (fixState === FIX_STATE.NOT_STARTED) { props.show(); } + if ( + fixState === FIX_STATE.COMPLETED && + largeThumbnailFiles.length > 0 + ) { + updateFixState(FIX_STATE.NOT_STARTED); + logError(Error(), 'large thumbnail files left after migration'); + } }; useEffect(() => { if (props.isOpen && fixState !== FIX_STATE.RUNNING) { From 2f52bc2aed118cb86aa7d0c8d9004ac8464072b7 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 26 Oct 2021 10:50:00 +0530 Subject: [PATCH 57/69] show compress thumbnail only if has large thumbnails and if no large thumbnail set state as completed --- src/components/FixLargeThumbnail.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 3db85a03a..08238182c 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -71,7 +71,10 @@ export default function FixLargeThumbnails(props: Props) { const main = async () => { const largeThumbnailFiles = await getLargeThumbnailFiles(); setLargeThumbnailFiles(largeThumbnailFiles ?? []); - if (fixState === FIX_STATE.NOT_STARTED) { + if ( + fixState === FIX_STATE.NOT_STARTED && + largeThumbnailFiles.length > 0 + ) { props.show(); } if ( @@ -81,6 +84,9 @@ export default function FixLargeThumbnails(props: Props) { updateFixState(FIX_STATE.NOT_STARTED); logError(Error(), 'large thumbnail files left after migration'); } + if (largeThumbnailFiles.length === 0) { + updateFixState(FIX_STATE.COMPLETED); + } }; useEffect(() => { if (props.isOpen && fixState !== FIX_STATE.RUNNING) { From 192229e1e1901f88d280d06ba975c527640a529a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 26 Oct 2021 11:36:58 +0530 Subject: [PATCH 58/69] reduce max-compression quality to 50% --- src/services/upload/thumbnailService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 0e2949343..725bc0e4e 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -8,7 +8,7 @@ const THUMBNAIL_HEIGHT = 720; const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10; export const MAX_THUMBNAIL_SIZE = 100 * 1024; const MIN_COMPRESSION_QUALITY = 0.7; -const MAX_COMPRESSION_QUALITY = 0.1; +const MAX_COMPRESSION_QUALITY = 0.5; const WAIT_TIME_THUMBNAIL_GENERATION = 10 * 1000; From e984eb9be9f9d00521407dee53aa373eae512fca Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 26 Oct 2021 12:10:05 +0530 Subject: [PATCH 59/69] improve variable names and fix compression loop condition --- src/services/upload/thumbnailService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 725bc0e4e..a0a9a5517 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -7,8 +7,8 @@ import FFmpegService from 'services/ffmpegService'; const THUMBNAIL_HEIGHT = 720; const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10; export const MAX_THUMBNAIL_SIZE = 100 * 1024; -const MIN_COMPRESSION_QUALITY = 0.7; -const MAX_COMPRESSION_QUALITY = 0.5; +const MIN_QUALITY = 0.5; +const MAX_QUALITY = 0.7; const WAIT_TIME_THUMBNAIL_GENERATION = 10 * 1000; @@ -175,7 +175,7 @@ export async function generateVideoThumbnail(file: globalThis.File) { async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { let thumbnailBlob: Blob = null; let prevSize = Number.MAX_SAFE_INTEGER; - let quality = MIN_COMPRESSION_QUALITY; + let quality = MAX_QUALITY; do { if (thumbnailBlob) { @@ -193,7 +193,7 @@ async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { thumbnailBlob = thumbnailBlob ?? new Blob([]); quality -= 0.1; } while ( - quality > MAX_COMPRESSION_QUALITY && + quality >= MIN_QUALITY && thumbnailBlob.size > MAX_THUMBNAIL_SIZE && percentageSizeDiff(thumbnailBlob.size, prevSize) >= MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF From 01e92a35e960c7c33f109512d808c501021b498b Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 26 Oct 2021 18:25:39 +0530 Subject: [PATCH 60/69] fetch large thumbnail after fix for confirmation --- src/components/FixLargeThumbnail.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index 08238182c..12de7731b 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -68,9 +68,14 @@ export default function FixLargeThumbnails(props: Props) { return fixState; }; + const fetchLargeThumbnail = async () => { + const largeThumbnailFiles = (await getLargeThumbnailFiles()) ?? []; + setLargeThumbnailFiles(largeThumbnailFiles); + return largeThumbnailFiles; + }; + const main = async () => { - const largeThumbnailFiles = await getLargeThumbnailFiles(); - setLargeThumbnailFiles(largeThumbnailFiles ?? []); + const largeThumbnailFiles = await fetchLargeThumbnail(); if ( fixState === FIX_STATE.NOT_STARTED && largeThumbnailFiles.length > 0 @@ -115,6 +120,7 @@ export default function FixLargeThumbnails(props: Props) { : FIX_STATE.COMPLETED ); } + await fetchLargeThumbnail(); }; const updateFixState = (fixState: FIX_STATE) => { From 29279771928a407134880d3e0359e554443a6f70 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 26 Oct 2021 18:30:59 +0530 Subject: [PATCH 61/69] update copy --- src/utils/strings/englishConstants.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 43104ba93..4cc26498b 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -557,9 +557,7 @@ const englishConstants = { CONFIRM_REMOVE_MESSAGE: () => ( <> -

- are you sure you want to remove these files from the collection? -

+

are you sure you want to remove these files from the album?

all files that are unique to this album will be moved to trash

From 23356bf1328382d8e80f104c9b1a80b3e522151a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 27 Oct 2021 08:46:06 +0530 Subject: [PATCH 62/69] update thubnail dimension calculation --- src/services/upload/thumbnailService.ts | 53 ++++++++++++++++++------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index a0a9a5517..85ab21193 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -4,14 +4,18 @@ import { logError } from 'utils/sentry'; import { BLACK_THUMBNAIL_BASE64 } from '../../../public/images/black-thumbnail-b64'; import FFmpegService from 'services/ffmpegService'; -const THUMBNAIL_HEIGHT = 720; +const MAX_THUMBNAIL_HEIGHT = 720; +const MAX_THUMBNAIL_WIDTH = 1280; const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10; export const MAX_THUMBNAIL_SIZE = 100 * 1024; const MIN_QUALITY = 0.5; const MAX_QUALITY = 0.7; const WAIT_TIME_THUMBNAIL_GENERATION = 10 * 1000; - +interface Dimension { + width: number; + height: number; +} export async function generateThumbnail( worker, file: globalThis.File, @@ -81,16 +85,18 @@ export async function generateImageThumbnail( await new Promise((resolve, reject) => { image.onload = () => { try { - const thumbnailWidth = - (image.width * THUMBNAIL_HEIGHT) / image.height; - canvas.width = thumbnailWidth; - canvas.height = THUMBNAIL_HEIGHT; + const thumbnailDimension = calculateThumbnailDimension({ + width: image.width, + height: image.height, + }); + canvas.width = thumbnailDimension.width; + canvas.height = thumbnailDimension.height; canvasCTX.drawImage( image, 0, 0, - thumbnailWidth, - THUMBNAIL_HEIGHT + thumbnailDimension.width, + thumbnailDimension.height ); image = null; clearTimeout(timeout); @@ -133,16 +139,18 @@ export async function generateVideoThumbnail(file: globalThis.File) { if (!video) { throw Error('video load failed'); } - const thumbnailWidth = - (video.videoWidth * THUMBNAIL_HEIGHT) / video.videoHeight; - canvas.width = thumbnailWidth; - canvas.height = THUMBNAIL_HEIGHT; + const thumbnailDimension = calculateThumbnailDimension({ + width: video.videoWidth, + height: video.videoHeight, + }); + canvas.width = thumbnailDimension.width; + canvas.height = thumbnailDimension.height; canvasCTX.drawImage( video, 0, 0, - thumbnailWidth, - THUMBNAIL_HEIGHT + thumbnailDimension.width, + thumbnailDimension.height ); video = null; clearTimeout(timeout); @@ -214,3 +222,20 @@ function percentageSizeDiff( ) { return ((oldThumbnailSize - newThumbnailSize) * 100) / oldThumbnailSize; } + +function calculateThumbnailDimension(OriginalDimension: Dimension): Dimension { + if (OriginalDimension.height === 0 || OriginalDimension.width === 0) { + return { width: 0, height: 0 }; + } + const widthScaleFactor = MAX_THUMBNAIL_WIDTH / OriginalDimension.width; + const heightScaleFactor = MAX_THUMBNAIL_HEIGHT / OriginalDimension.height; + const scaleFactor = Math.min(widthScaleFactor, heightScaleFactor); + const thumbnailDimension = { + width: Math.round(OriginalDimension.width * scaleFactor), + height: Math.round(OriginalDimension.height * scaleFactor), + }; + if (thumbnailDimension.width === 0 || thumbnailDimension.height === 0) { + return { width: 0, height: 0 }; + } + return thumbnailDimension; +} From 01dc6142d0d314e96ead5069800772f7e79497a5 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 27 Oct 2021 08:53:29 +0530 Subject: [PATCH 63/69] log thumbnail dimension too --- src/services/upload/thumbnailService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 85ab21193..b2725d3d1 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -3,6 +3,7 @@ import { CustomError, errorWithContext } from 'utils/common/errorUtil'; import { logError } from 'utils/sentry'; import { BLACK_THUMBNAIL_BASE64 } from '../../../public/images/black-thumbnail-b64'; import FFmpegService from 'services/ffmpegService'; +import { convertToHumanReadable } from 'utils/billingUtil'; const MAX_THUMBNAIL_HEIGHT = 720; const MAX_THUMBNAIL_WIDTH = 1280; @@ -209,7 +210,8 @@ async function thumbnailCanvasToBlob(canvas: HTMLCanvasElement) { if (thumbnailBlob.size > MAX_THUMBNAIL_SIZE) { logError( Error('thumbnail_too_large'), - 'thumbnail greater than max limit' + 'thumbnail greater than max limit', + { thumbnailSize: convertToHumanReadable(thumbnailBlob.size) } ); } From 60c820a9be3e42e667a32ad94b7bb6a218039a21 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 27 Oct 2021 08:59:02 +0530 Subject: [PATCH 64/69] refactor code --- src/services/upload/thumbnailService.ts | 28 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index b2725d3d1..0bf7642ff 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -5,8 +5,7 @@ import { BLACK_THUMBNAIL_BASE64 } from '../../../public/images/black-thumbnail-b import FFmpegService from 'services/ffmpegService'; import { convertToHumanReadable } from 'utils/billingUtil'; -const MAX_THUMBNAIL_HEIGHT = 720; -const MAX_THUMBNAIL_WIDTH = 1280; +const MAX_THUMBNAIL_DIMENSION: Dimension = { width: 1280, height: 720 }; const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10; export const MAX_THUMBNAIL_SIZE = 100 * 1024; const MIN_QUALITY = 0.5; @@ -86,10 +85,14 @@ export async function generateImageThumbnail( await new Promise((resolve, reject) => { image.onload = () => { try { - const thumbnailDimension = calculateThumbnailDimension({ + const imageDimension = { width: image.width, height: image.height, - }); + }; + const thumbnailDimension = calculateThumbnailDimension( + imageDimension, + MAX_THUMBNAIL_DIMENSION + ); canvas.width = thumbnailDimension.width; canvas.height = thumbnailDimension.height; canvasCTX.drawImage( @@ -140,10 +143,14 @@ export async function generateVideoThumbnail(file: globalThis.File) { if (!video) { throw Error('video load failed'); } - const thumbnailDimension = calculateThumbnailDimension({ + const videoDimension = { width: video.videoWidth, height: video.videoHeight, - }); + }; + const thumbnailDimension = calculateThumbnailDimension( + videoDimension, + MAX_THUMBNAIL_DIMENSION + ); canvas.width = thumbnailDimension.width; canvas.height = thumbnailDimension.height; canvasCTX.drawImage( @@ -225,12 +232,15 @@ function percentageSizeDiff( return ((oldThumbnailSize - newThumbnailSize) * 100) / oldThumbnailSize; } -function calculateThumbnailDimension(OriginalDimension: Dimension): Dimension { +function calculateThumbnailDimension( + OriginalDimension: Dimension, + maxDimension: Dimension +): Dimension { if (OriginalDimension.height === 0 || OriginalDimension.width === 0) { return { width: 0, height: 0 }; } - const widthScaleFactor = MAX_THUMBNAIL_WIDTH / OriginalDimension.width; - const heightScaleFactor = MAX_THUMBNAIL_HEIGHT / OriginalDimension.height; + const widthScaleFactor = maxDimension.width / OriginalDimension.width; + const heightScaleFactor = maxDimension.height / OriginalDimension.height; const scaleFactor = Math.min(widthScaleFactor, heightScaleFactor); const thumbnailDimension = { width: Math.round(OriginalDimension.width * scaleFactor), From 81c77790dfbe1dc93a69813e67e0cf1f93825790 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 27 Oct 2021 09:06:27 +0530 Subject: [PATCH 65/69] add comments --- src/services/upload/thumbnailService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 0bf7642ff..568621d29 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -232,6 +232,8 @@ function percentageSizeDiff( return ((oldThumbnailSize - newThumbnailSize) * 100) / oldThumbnailSize; } +// method to calculate new size of image for limiting it to maximum width and height, maintaining aspect ratio +// returns {0,0} for invalid inputs function calculateThumbnailDimension( OriginalDimension: Dimension, maxDimension: Dimension From 242deebb4c48ed602b65db92143b0f37e81249be Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 27 Oct 2021 10:30:57 +0530 Subject: [PATCH 66/69] make max dimension(width or height) = 720px --- src/services/upload/thumbnailService.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index 568621d29..a30a31af4 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -5,17 +5,19 @@ import { BLACK_THUMBNAIL_BASE64 } from '../../../public/images/black-thumbnail-b import FFmpegService from 'services/ffmpegService'; import { convertToHumanReadable } from 'utils/billingUtil'; -const MAX_THUMBNAIL_DIMENSION: Dimension = { width: 1280, height: 720 }; +const MAX_THUMBNAIL_DIMENSION = 720; const MIN_COMPRESSION_PERCENTAGE_SIZE_DIFF = 10; export const MAX_THUMBNAIL_SIZE = 100 * 1024; const MIN_QUALITY = 0.5; const MAX_QUALITY = 0.7; const WAIT_TIME_THUMBNAIL_GENERATION = 10 * 1000; + interface Dimension { width: number; height: number; } + export async function generateThumbnail( worker, file: globalThis.File, @@ -236,13 +238,13 @@ function percentageSizeDiff( // returns {0,0} for invalid inputs function calculateThumbnailDimension( OriginalDimension: Dimension, - maxDimension: Dimension + maxDimension: number ): Dimension { if (OriginalDimension.height === 0 || OriginalDimension.width === 0) { return { width: 0, height: 0 }; } - const widthScaleFactor = maxDimension.width / OriginalDimension.width; - const heightScaleFactor = maxDimension.height / OriginalDimension.height; + const widthScaleFactor = maxDimension / OriginalDimension.width; + const heightScaleFactor = maxDimension / OriginalDimension.height; const scaleFactor = Math.min(widthScaleFactor, heightScaleFactor); const thumbnailDimension = { width: Math.round(OriginalDimension.width * scaleFactor), From e850c8c24286212719a18ff5de8d35269a6dfe31 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 27 Oct 2021 10:55:43 +0530 Subject: [PATCH 67/69] fix typo --- src/services/upload/thumbnailService.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index a30a31af4..586a396af 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -237,18 +237,18 @@ function percentageSizeDiff( // method to calculate new size of image for limiting it to maximum width and height, maintaining aspect ratio // returns {0,0} for invalid inputs function calculateThumbnailDimension( - OriginalDimension: Dimension, + originalDimension: Dimension, maxDimension: number ): Dimension { - if (OriginalDimension.height === 0 || OriginalDimension.width === 0) { + if (originalDimension.height === 0 || originalDimension.width === 0) { return { width: 0, height: 0 }; } - const widthScaleFactor = maxDimension / OriginalDimension.width; - const heightScaleFactor = maxDimension / OriginalDimension.height; + const widthScaleFactor = maxDimension / originalDimension.width; + const heightScaleFactor = maxDimension / originalDimension.height; const scaleFactor = Math.min(widthScaleFactor, heightScaleFactor); const thumbnailDimension = { - width: Math.round(OriginalDimension.width * scaleFactor), - height: Math.round(OriginalDimension.height * scaleFactor), + width: Math.round(originalDimension.width * scaleFactor), + height: Math.round(originalDimension.height * scaleFactor), }; if (thumbnailDimension.width === 0 || thumbnailDimension.height === 0) { return { width: 0, height: 0 }; From b587ea70faaddca167e7f96512b547b4cec98fa3 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 27 Oct 2021 12:43:03 +0530 Subject: [PATCH 68/69] fix file name of ffmpeg in-memory file --- src/services/ffmpegService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/services/ffmpegService.ts b/src/services/ffmpegService.ts index 13b7bde7e..fb0e1d2e3 100644 --- a/src/services/ffmpegService.ts +++ b/src/services/ffmpegService.ts @@ -44,8 +44,8 @@ class FFmpegService { async function generateThumbnailHelper(ffmpeg: FFmpeg, file: File) { try { - const inputFileName = `${Date.now().toString}-${file.name}`; - const thumbFileName = `${Date.now().toString}-thumb.jpeg`; + const inputFileName = `${Date.now().toString()}-${file.name}`; + const thumbFileName = `${Date.now().toString()}-thumb.jpeg`; ffmpeg.FS( 'writeFile', inputFileName, @@ -62,7 +62,8 @@ async function generateThumbnailHelper(ffmpeg: FFmpeg, file: File) { `00:00:0${seekTime.toFixed(3)}`, '-vframes', '1', - '-vf scale=512:512', + '-vf', + 'scale=512:512', thumbFileName ); thumb = ffmpeg.FS('readFile', thumbFileName); From b07ffbd820be99a0e1fc7a7c5ffb1125a30d398e Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 27 Oct 2021 12:51:15 +0530 Subject: [PATCH 69/69] make ffmpeg thumbnail output height to 720 --- src/services/ffmpegService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/ffmpegService.ts b/src/services/ffmpegService.ts index fb0e1d2e3..4cfcdf9be 100644 --- a/src/services/ffmpegService.ts +++ b/src/services/ffmpegService.ts @@ -63,7 +63,7 @@ async function generateThumbnailHelper(ffmpeg: FFmpeg, file: File) { '-vframes', '1', '-vf', - 'scale=512:512', + 'scale=-1:720', thumbFileName ); thumb = ffmpeg.FS('readFile', thumbFileName);