pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
at main 215 lines 7.4 kB view raw
1import { useCallback } from "react"; 2 3// import { SessionResponse } from "@/backend/accounts/auth"; 4import { bookmarkMediaToInput } from "@/backend/accounts/bookmarks"; 5import { 6 base64ToBuffer, 7 bytesToBase64, 8 bytesToBase64Url, 9 encryptData, 10 // keysFromMnemonic, 11 keysFromSeed, 12 signChallenge, 13} from "@/backend/accounts/crypto"; 14import { 15 importBookmarks, 16 importGroupOrder, 17 importProgress, 18 importSettings, 19 importWatchHistory, 20} from "@/backend/accounts/import"; 21// import { getLoginChallengeToken, loginAccount } from "@/backend/accounts/login"; 22import { progressMediaItemToInputs } from "@/backend/accounts/progress"; 23import { 24 getRegisterChallengeToken, 25 registerAccount, 26} from "@/backend/accounts/register"; 27import { watchHistoryItemsToInputs } from "@/backend/accounts/watchHistory"; 28// import { removeSession } from "@/backend/accounts/sessions"; 29// import { getSettings } from "@/backend/accounts/settings"; 30// import { 31// UserResponse, 32// getBookmarks, 33// getProgress, 34// getUser, 35// } from "@/backend/accounts/user"; 36import { useAuthData } from "@/hooks/auth/useAuthData"; 37// import { useBackendUrl } from "@/hooks/auth/useBackendUrl"; 38import { AccountWithToken, useAuthStore } from "@/stores/auth"; 39import { BookmarkMediaItem, useBookmarkStore } from "@/stores/bookmarks"; 40import { useGroupOrderStore } from "@/stores/groupOrder"; 41import { usePreferencesStore } from "@/stores/preferences"; 42import { ProgressMediaItem, useProgressStore } from "@/stores/progress"; 43import { useSubtitleStore } from "@/stores/subtitles"; 44import { WatchHistoryItem, useWatchHistoryStore } from "@/stores/watchHistory"; 45 46export interface RegistrationData { 47 recaptchaToken?: string; 48 mnemonic: string; 49 userData: { 50 device: string; 51 profile: { 52 colorA: string; 53 colorB: string; 54 icon: string; 55 }; 56 }; 57} 58 59export interface LoginData { 60 mnemonic: string; 61 userData: { 62 device: string; 63 }; 64} 65 66export function useMigration() { 67 const currentAccount = useAuthStore((s) => s.account); 68 const progress = useProgressStore((s) => s.items); 69 const watchHistory = useWatchHistoryStore((s) => s.items); 70 const bookmarks = useBookmarkStore((s) => s.bookmarks); 71 const groupOrder = useGroupOrderStore((s) => s.groupOrder); 72 const preferences = usePreferencesStore.getState(); 73 const subtitleLanguage = useSubtitleStore((s) => s.lastSelectedLanguage); 74 const { login: userDataLogin } = useAuthData(); 75 76 const migrate = useCallback( 77 async (backendUrl: string, recaptchaToken?: string) => { 78 if (!currentAccount) return; 79 80 const importData = async ( 81 backendUrlInner: string, 82 account: AccountWithToken, 83 progressItems: Record<string, ProgressMediaItem>, 84 watchHistoryItems: Record<string, WatchHistoryItem>, 85 bookmarkItems: Record<string, BookmarkMediaItem>, 86 groupOrderItems: string[], 87 ) => { 88 if ( 89 Object.keys(progressItems).length === 0 && 90 Object.keys(watchHistoryItems).length === 0 && 91 Object.keys(bookmarkItems).length === 0 && 92 groupOrderItems.length === 0 93 ) { 94 return; 95 } 96 97 const progressInputs = Object.entries(progressItems).flatMap( 98 ([tmdbId, item]) => progressMediaItemToInputs(tmdbId, item), 99 ); 100 101 const watchHistoryInputs = watchHistoryItemsToInputs(watchHistoryItems); 102 103 const bookmarkInputs = Object.entries(bookmarkItems).map( 104 ([tmdbId, item]) => bookmarkMediaToInput(tmdbId, item), 105 ); 106 107 const importPromises = [ 108 importProgress(backendUrlInner, account, progressInputs), 109 importWatchHistory(backendUrlInner, account, watchHistoryInputs), 110 importBookmarks(backendUrlInner, account, bookmarkInputs), 111 ]; 112 113 // Import group order if it exists 114 if (groupOrderItems.length > 0) { 115 importPromises.push( 116 importGroupOrder(backendUrlInner, account, groupOrderItems), 117 ); 118 } 119 120 // Import settings/preferences 121 importPromises.push( 122 importSettings(backendUrlInner, account, { 123 defaultSubtitleLanguage: subtitleLanguage || undefined, 124 febboxKey: preferences.febboxKey, 125 debridToken: preferences.debridToken, 126 debridService: preferences.debridService, 127 enableThumbnails: preferences.enableThumbnails, 128 enableAutoplay: preferences.enableAutoplay, 129 enableSkipCredits: preferences.enableSkipCredits, 130 enableDiscover: preferences.enableDiscover, 131 enableFeatured: preferences.enableFeatured, 132 enableDetailsModal: preferences.enableDetailsModal, 133 enableImageLogos: preferences.enableImageLogos, 134 enableCarouselView: preferences.enableCarouselView, 135 forceCompactEpisodeView: preferences.forceCompactEpisodeView, 136 sourceOrder: 137 preferences.sourceOrder.length > 0 138 ? preferences.sourceOrder 139 : undefined, 140 enableSourceOrder: preferences.enableSourceOrder, 141 lastSuccessfulSource: preferences.lastSuccessfulSource, 142 enableLastSuccessfulSource: preferences.enableLastSuccessfulSource, 143 embedOrder: 144 preferences.embedOrder.length > 0 145 ? preferences.embedOrder 146 : undefined, 147 enableEmbedOrder: preferences.enableEmbedOrder, 148 proxyTmdb: preferences.proxyTmdb, 149 enableLowPerformanceMode: preferences.enableLowPerformanceMode, 150 enableNativeSubtitles: preferences.enableNativeSubtitles, 151 enableHoldToBoost: preferences.enableHoldToBoost, 152 homeSectionOrder: 153 preferences.homeSectionOrder.length > 0 154 ? preferences.homeSectionOrder 155 : undefined, 156 manualSourceSelection: preferences.manualSourceSelection, 157 enableDoubleClickToSeek: preferences.enableDoubleClickToSeek, 158 enableAutoResumeOnPlaybackError: 159 preferences.enableAutoResumeOnPlaybackError, 160 }), 161 ); 162 163 await Promise.all(importPromises); 164 }; 165 166 const { challenge } = await getRegisterChallengeToken( 167 backendUrl, 168 recaptchaToken || undefined, // Pass undefined if token is not provided 169 ); 170 const keys = await keysFromSeed(base64ToBuffer(currentAccount.seed)); 171 const signature = await signChallenge(keys, challenge); 172 const registerResult = await registerAccount(backendUrl, { 173 challenge: { 174 code: challenge, 175 signature, 176 }, 177 publicKey: bytesToBase64Url(keys.publicKey), 178 device: await encryptData(currentAccount.deviceName, keys.seed), 179 profile: currentAccount.profile, 180 }); 181 182 const account = await userDataLogin( 183 registerResult, 184 registerResult.user, 185 registerResult.session, 186 bytesToBase64(keys.seed), 187 ); 188 189 await importData( 190 backendUrl, 191 account, 192 progress, 193 watchHistory, 194 bookmarks, 195 groupOrder, 196 ); 197 198 return account; 199 }, 200 [ 201 currentAccount, 202 userDataLogin, 203 bookmarks, 204 progress, 205 watchHistory, 206 groupOrder, 207 preferences, 208 subtitleLanguage, 209 ], 210 ); 211 212 return { 213 migrate, 214 }; 215}