webminidisc/static/js/main.10b5445b.chunk.js.map

1 line
142 KiB
Plaintext

{"version":3,"sources":["images/chrome-icon.svg","webpack:///./src/services/atracdenc-worker.ts?dc3c","serviceWorker.ts","utils.ts","services/netmd.ts","services/registry.ts","redux/upload-dialog-feature.ts","redux/rename-dialog-feature.ts","redux/error-dialog-feature.ts","redux/convert-dialog-feature.ts","redux/dump-dialog-feature.ts","redux/record-dialog-feature.ts","redux/app-feature.ts","redux/main-feature.ts","redux/store.ts","redux/actions.ts","components/about-dialog.tsx","components/topmenu.tsx","components/welcome.tsx","components/rename-dialog.tsx","components/upload-dialog.tsx","components/record-dialog.tsx","components/error-dialog.tsx","components/convert-dialog.tsx","components/controls.tsx","components/dump-dialog.tsx","components/main.tsx","services/atracdenc-worker.ts","components/app.tsx","services/audio-export.ts","index.tsx","services/mediarecorder.ts"],"names":["module","exports","Worker","isLocalhost","Boolean","window","location","hostname","match","registerValidSW","swUrl","config","navigator","serviceWorker","register","then","registration","onupdatefound","installingWorker","installing","onstatechange","console","log","state","controller","onUpdate","onSuccess","update","catch","error","sleep","ms","Promise","resolve","setTimeout","async","sleepWithProgressCallback","cb","elapsedSecs","interval","setInterval","Math","min","clearInterval","useShallowEqualSelector","selector","useSelector","shallowEqual","getPublicPathFor","script","sanitizeTitle","title","normalize","replace","require","ServiceRegistry","slice","createSlice","name","initialState","visible","writtenProgress","encryptedProgress","totalProgress","trackTotal","trackConverting","trackCurrent","titleCurrent","titleConverting","reducers","setVisible","action","payload","setWriteProgress","encrypted","written","total","setTrackProgress","current","converting","reducer","actions","enableBatching","index","setCurrentName","setIndex","setErrorMessage","format","setFormat","inputDeviceId","setInputDeviceId","trackDone","setProgress","mainView","loading","pairingFailed","pairingMessage","browserSupported","darkMode","key","defaultValue","res","localStorage","getItem","JSON","parse","e","loadPreference","aboutDialogVisible","setState","setLoading","setPairingFailed","setPairingMessage","setBrowserSupported","setDarkMode","value","setItem","stringify","showAboutDialog","disc","deviceName","setDisc","setDeviceName","store","configureStore","renameDialog","uploadDialog","errorDialog","convertDialog","dumpDialog","recordDialog","appState","main","middleware","getDefaultMiddleware","listContent","dispatch","appStateActions","serviceRegistry","netmdService","getDeviceName","batchActions","mainActions","WireformatDict","SP","Wireformat","pcm","LP2","lp2","LP105","l105kbps","LP4","lp4","convertAndUpload","files","getState","audioExportService","wireformat","uploadDialogActions","updateProgressCallback","trackUpdate","length","updateTrack","maxTitleLength","getTracks","map","track","reduce","acc","floor","errorMessage","i","item","converted","convertNext","f","push","reject","data","prepare","export","file","err","j","conversionIterator","extStartIndex","lastIndexOf","substring","upload","actionToDispatch","concat","errorDialogAction","Transition","React","forwardRef","props","ref","Slide","direction","AboutDialog","useDispatch","Dialog","open","maxWidth","fullWidth","TransitionComponent","aria-labelledby","DialogTitle","id","DialogContent","DialogContentText","Link","rel","href","target","DialogActions","Button","onClick","appActions","TopMenu","useState","menuAnchorEl","setMenuAnchorEl","menuOpen","handleMenuClose","menuItems","MenuItem","renameDialogActions","wipeDisc","Fragment","IconButton","aria-label","aria-controls","aria-haspopup","event","currentTarget","Menu","anchorEl","keepMounted","onClose","useStyles","makeStyles","theme","position","flex","display","justifyContent","flexDirection","alignItems","button","marginTop","spacing","minWidth","chromeLogo","width","height","why","alignSelf","headBox","Welcome","classes","toLowerCase","showWhyUnsupported","setWhyUnsupported","Box","className","Typography","component","variant","align","color","init","connect","pair","message","FormControl","style","visibility","FormHelperText","preventDefault","alt","src","ChromeIconPath","RenameDialog","renameDialogVisible","renameDialogTitle","renameDialogIndex","what","handleCancelRename","handleDoRename","newName","renameDisc","renameTrack","TextField","autoFocus","label","type","onKeyDown","onChange","progressPerc","progressBar","uploadLabel","UploadDialog","progressValue","bufferValue","convertedValue","aria-describedby","LinearProgress","valueBuffer","RecordDialog","round","ErrorDialog","errorDialogActions","container","formControl","ConvertDialog","handleClose","convertDialogActions","InputLabel","Select","labelId","ev","input","Input","Controls","handlePrev","useCallback","prev","handlePlay","play","handleStop","stop","handleNext","next","marginRight","selectEmpty","head","textShadow","fontSize","typography","h2","textAlign","marginBottom","DumpDialog","trackIndexes","devices","setDevices","mediaRecorderService","stopTestInput","dumpDialogActions","handleChange","deviceId","playTestInput","handleStartTransfer","indexes","recordDialogAction","tracks","filter","t","indexOf","entries","gotoTrack","getPosition","expected","every","_","pause","initStream","startRecording","durationInSec","duration","perc","stopRecording","downloadRecorded","closeStream","useEffect","mediaDevices","getUserMedia","audio","inputDevices","enumerateDevices","device","kind","updateDeviceList","displayEmpty","disabled","add","bottom","right","overflowY","marginLeft","outline","toolbar","breakpoints","up","toolbarLabel","toolbarHighlight","palette","secondary","backgroundColor","lighten","light","text","primary","dark","formatBadge","BadgeImpl","badge","colorPrimary","border","background","paper","padding","titleCell","overflow","textOverflow","indexCell","whiteSpace","paddingRight","backdrop","zIndex","drawer","EncodingName","Encoding","sp","Main","selected","setSelected","selectedCount","moveMenuAnchorEl","setMoveMenuAnchorEl","handleShowMoveMenu","handleCloseMoveMenu","handleMoveSelectedTrack","destIndex","srcIndex","moveTrack","handleShowDumpDialog","uploadedFiles","setUploadedFiles","onDrop","acceptedFiles","rejectedFiles","getRootProps","getInputProps","isDragActive","useDropzone","accept","noClick","group","groups","encoding","formatTimeFromFrames","sort","l","r","handleRenameDoubleClick","selectedIndex","currentName","find","left","Toolbar","clsx","Checkbox","indeterminate","checked","inputProps","Tooltip","PaperProps","maxHeight","Array","fill","reverse","deleteTrack","Table","size","TableHead","TableRow","TableCell","TableBody","hover","includes","onDoubleClick","handleSelectClick","Backdrop","Fab","Module","layout","copyright","down","flexWrap","minidiscLogo","controlsContainer","paddingLeft","order","darkTheme","createMuiTheme","contrastText","lightTheme","App","ThemeProvider","CssBaseline","Paper","undefined","Date","getFullYear","data-via","data-hashtags","data-dnt","data-show-count","CircularProgress","AtracdencProcess","worker","messageCallback","onmessage","this","handleMessage","bind","postMessage","bitrate","eventData","result","terminate","WorkerGlobalScope","self","a","others","importScripts","m","setLogger","msg","stream","dataArray","Uint8Array","FS","writeFile","callMain","fileStat","stat","tmp","outAt3FileStream","read","close","buffer","AtracdencWorker","FFMpegAudioExportService","ffmpegProcess","atracdencProcess","loglines","inFileName","outFileNameNoExt","setLogging","createWorker","logger","corePath","workerPath","load","ext","split","Error","write","transcode","audioFormatRegex","inputFormatRegex","line","outFileName","encode","constructor","debug","netmdInterface","_fn","args","method","info","warn","child","iface","openNewDevice","usb","openPairedDevice","netMd","finalize","setTrackTitle","oldName","getDiscTitle","newNameWithGroups","_getDiscTitle","cacheTOC","setDiscTitle","syncTOC","eraseTrack","eraseDisc","dst","progressCallback","byteLength","updateProgress","w","webWorkerAsyncPacketIterator","makeGetAsyncPacketIteratorOnWorkerThread","encryptedBytes","mdTrack","MDTrack","download","writtenBytes","nextTrack","previousTrack","recorder","audioContext","analyserNode","gainNode","AudioContext","createGain","createAnalyser","createMediaStreamSource","destination","recordConstraints","autoGainControl","channelCount","echoCancellation","noiseSuppression","sampleRate","highpassFilter","audioTracks","getAudioTracks","getSettings","Recorder","record","forEach","exportWAV","url","URL","createObjectURL","document","createElement","body","appendChild","click","revokeObjectURL","removeChild","deferredPrompt","addEventListener","returnValue","ondisconnect","ReactDOM","render","getElementById","process","origin","fetch","headers","response","contentType","get","status","ready","unregister","reload","checkValidServiceWorker"],"mappings":"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","file":"static/js/main.10b5445b.chunk.js","sourcesContent":["module.exports = __webpack_public_path__ + \"static/media/chrome-icon.f3b6c54c.svg\";","module.exports = function() {\n return new Worker(__webpack_public_path__ + \"2fad141346134fe4e0c3.worker.js\");\n};","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(/^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)\n);\n\ntype Config = {\n onSuccess?: (registration: ServiceWorkerRegistration) => void;\n onUpdate?: (registration: ServiceWorkerRegistration) => void;\n};\n\nexport function register(config?: Config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/final-service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl: string, config?: Config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n console.log('state change', installingWorker.state);\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n registration.update(); // Check for new version everytime we load the page\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl: string, config?: Config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' },\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log('No internet connection found. App is running in offline mode.');\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import { useSelector, shallowEqual } from 'react-redux';\nimport { RootState } from './redux/store';\n\nexport function sleep(ms: number) {\n return new Promise(resolve => {\n setTimeout(resolve, ms);\n });\n}\n\nexport async function sleepWithProgressCallback(ms: number, cb: (perc: number) => void) {\n let elapsedSecs = 1;\n let interval = setInterval(() => {\n elapsedSecs++;\n cb(Math.min(100, ((elapsedSecs * 1000) / ms) * 100));\n }, 1000);\n await sleep(ms);\n window.clearInterval(interval);\n}\n\nexport function useShallowEqualSelector<TState = RootState, TSelected = unknown>(selector: (state: TState) => TSelected): TSelected {\n return useSelector(selector, shallowEqual);\n}\n\nexport function debugEnabled() {\n return process.env.NODE_ENV === 'development';\n}\n\nexport function getPublicPathFor(script: string) {\n return `${process.env.PUBLIC_URL}/${script}`;\n}\n\nexport function savePreference(key: string, value: unknown) {\n localStorage.setItem(key, JSON.stringify(value));\n}\n\nexport function loadPreference<T>(key: string, defaultValue: T): T {\n let res = localStorage.getItem(key);\n if (res === null) {\n return defaultValue;\n } else {\n try {\n return JSON.parse(res) as T;\n } catch (e) {\n return defaultValue;\n }\n }\n}\n\nexport function getAvailableCharsForTrackTitle(trackTitles: string[]) {\n const maxChars = 1700; // see https://www.minidisc.org/md_toc.html\n const usedChars = trackTitles.reduce((acc, title) => {\n return acc + title.length;\n }, 0);\n return maxChars - usedChars;\n}\n\nexport function framesToSec(frames: number) {\n return frames / 512;\n}\n\nexport function sanitizeTitle(title: string) {\n return title.normalize('NFD').replace(/[^\\x00-\\x7F]/g, '');\n}\n\ndeclare let process: any;\n","import { openNewDevice, NetMDInterface, Disc, listContent, openPairedDevice, Wireformat, MDTrack, download } from 'netmd-js';\nimport { makeGetAsyncPacketIteratorOnWorkerThread } from 'netmd-js/dist/web-encrypt-worker';\nimport { Logger, ConsoleLogger, Level } from 'netmd-js/dist/logger';\nimport { sanitizeTitle, sleep } from '../utils';\n\nconst Worker = require('worker-loader!netmd-js/dist/web-encrypt-worker.js'); // eslint-disable-line import/no-webpack-loader-syntax\n\nexport interface NetMDService {\n pair(): Promise<boolean>;\n connect(): Promise<boolean>;\n listContent(): Promise<Disc>;\n getDeviceName(): Promise<string>;\n finalize(): Promise<void>;\n renameTrack(index: number, newTitle: string): Promise<void>;\n renameDisc(newName: string): Promise<void>;\n deleteTrack(index: number): Promise<void>;\n moveTrack(src: number, dst: number): Promise<void>;\n wipeDisc(): Promise<void>;\n upload(\n title: string,\n data: ArrayBuffer,\n format: Wireformat,\n progressCallback: (progress: { written: number; encrypted: number; total: number }) => void\n ): Promise<void>;\n\n play(): Promise<void>;\n pause(): Promise<void>;\n stop(): Promise<void>;\n next(): Promise<void>;\n prev(): Promise<void>;\n gotoTrack(index: number): Promise<void>;\n getPosition(): Promise<number[] | null>;\n}\n\nexport class NetMDUSBService implements NetMDService {\n private netmdInterface?: NetMDInterface;\n private logger?: Logger;\n\n constructor({ debug = false }: { debug: boolean }) {\n if (debug) {\n // Logging a few methods that have been causing issues with some units\n const _fn = (...args: any) => {\n if (args && args[0] && args[0].method) {\n console.log(...args);\n }\n };\n this.logger = {\n debug: _fn,\n info: _fn,\n warn: _fn,\n error: _fn,\n child: () => this.logger!,\n };\n }\n }\n\n async pair() {\n let iface = await openNewDevice(navigator.usb, this.logger);\n if (iface === null) {\n return false;\n }\n this.netmdInterface = iface;\n return true;\n }\n\n async connect() {\n let iface = await openPairedDevice(navigator.usb, this.logger);\n if (iface === null) {\n return false;\n }\n this.netmdInterface = iface;\n return true;\n }\n\n async listContent() {\n return await listContent(this.netmdInterface!);\n }\n\n async getDeviceName() {\n return await this.netmdInterface!.netMd.getDeviceName();\n }\n\n async finalize() {\n await this.netmdInterface!.netMd.finalize();\n }\n\n async renameTrack(index: number, title: string) {\n // Removing non ascii chars... Sorry, I didn't implement char encoding.\n title = sanitizeTitle(title);\n await this.netmdInterface!.setTrackTitle(index, title);\n }\n\n async renameDisc(newName: string) {\n const oldName = await this.netmdInterface!.getDiscTitle();\n let newNameWithGroups = await this.netmdInterface!._getDiscTitle();\n newNameWithGroups = newNameWithGroups.replace(oldName, newName);\n await this.netmdInterface!.cacheTOC();\n await this.netmdInterface!.setDiscTitle(newNameWithGroups);\n await this.netmdInterface!.syncTOC();\n }\n\n async deleteTrack(index: number) {\n await this.netmdInterface!.eraseTrack(index);\n await sleep(100);\n }\n\n async wipeDisc() {\n await this.netmdInterface!.eraseDisc();\n }\n\n async moveTrack(src: number, dst: number) {\n await this.netmdInterface!.moveTrack(src, dst);\n }\n\n async upload(\n title: string,\n data: ArrayBuffer,\n format: Wireformat,\n progressCallback: (progress: { written: number; encrypted: number; total: number }) => void\n ) {\n let total = data.byteLength;\n let written = 0;\n let encrypted = 0;\n function updateProgress() {\n progressCallback({ written, encrypted, total });\n }\n\n let w = new Worker();\n\n let webWorkerAsyncPacketIterator = makeGetAsyncPacketIteratorOnWorkerThread(w, ({ encryptedBytes }) => {\n encrypted = encryptedBytes;\n updateProgress();\n });\n\n // Removing non ascii chars... Sorry, I didn't implement char encoding.\n title = sanitizeTitle(title);\n let mdTrack = new MDTrack(title, format, data, 0x80000, webWorkerAsyncPacketIterator);\n\n await download(this.netmdInterface!, mdTrack, ({ writtenBytes }) => {\n written = writtenBytes;\n updateProgress();\n });\n\n w.terminate();\n }\n\n async play() {\n await this.netmdInterface!.play();\n }\n async pause() {\n await this.netmdInterface!.pause();\n }\n async stop() {\n await this.netmdInterface!.stop();\n }\n async next() {\n await this.netmdInterface!.nextTrack();\n }\n async prev() {\n await this.netmdInterface!.previousTrack();\n }\n async gotoTrack(index: number) {\n await this.netmdInterface!.gotoTrack(index);\n }\n async getPosition() {\n return await this.netmdInterface!.getPosition();\n }\n}\n","import { NetMDService } from './netmd';\nimport { AudioExportService } from './audio-export';\nimport { MediaRecorderService } from './mediarecorder';\n\ninterface ServiceRegistry {\n netmdService?: NetMDService;\n audioExportService?: AudioExportService;\n mediaRecorderService?: MediaRecorderService;\n}\n\nconst ServiceRegistry: ServiceRegistry = {};\n\nexport default ServiceRegistry;\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface LoadingDialogState {\n visible: boolean;\n writtenProgress: number;\n encryptedProgress: number;\n totalProgress: number;\n\n trackTotal: number;\n trackConverting: number;\n trackCurrent: number;\n\n titleCurrent: string;\n titleConverting: string;\n}\n\nconst initialState: LoadingDialogState = {\n visible: false,\n // Current Track Upload\n writtenProgress: 0,\n encryptedProgress: 0,\n totalProgress: 1,\n\n // Tracks done\n trackTotal: 1,\n trackConverting: 0,\n trackCurrent: 0,\n titleCurrent: '',\n titleConverting: '',\n};\n\nexport const slice = createSlice({\n name: 'uploadDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction<boolean>) => {\n state.visible = action.payload;\n },\n setWriteProgress: (state, action: PayloadAction<{ written: number; encrypted: number; total: number }>) => {\n state.encryptedProgress = action.payload.encrypted;\n state.writtenProgress = action.payload.written;\n state.totalProgress = action.payload.total;\n },\n setTrackProgress: (\n state,\n action: PayloadAction<{ total: number; current: number; converting: number; titleCurrent: string; titleConverting: string }>\n ) => {\n state.trackTotal = action.payload.total;\n state.trackCurrent = action.payload.current;\n state.trackConverting = action.payload.converting;\n state.titleCurrent = action.payload.titleCurrent;\n state.titleConverting = action.payload.titleConverting;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface RenameDialogState {\n visible: boolean;\n title: string;\n index: number;\n}\n\nconst initialState: RenameDialogState = {\n visible: false,\n title: '',\n index: -1,\n};\n\nexport const slice = createSlice({\n name: 'renameDialog',\n initialState,\n reducers: {\n setVisible: (state: RenameDialogState, action: PayloadAction<boolean>) => {\n state.visible = action.payload;\n },\n setCurrentName: (state: RenameDialogState, action: PayloadAction<string>) => {\n state.title = action.payload;\n },\n setIndex: (state: RenameDialogState, action: PayloadAction<number>) => {\n state.index = action.payload;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface ErrorDialogState {\n visible: boolean;\n error: string;\n}\n\nconst initialState: ErrorDialogState = {\n visible: false,\n error: ``,\n};\n\nconst slice = createSlice({\n name: 'errorDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction<boolean>) => {\n state.visible = action.payload;\n },\n setErrorMessage: (state, action: PayloadAction<string>) => {\n state.error = `${action.payload}`;\n },\n },\n});\n\nexport const { actions, reducer } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface ConvertDialogFeature {\n visible: boolean;\n format: string;\n}\n\nconst initialState: ConvertDialogFeature = {\n visible: false,\n format: `LP2`,\n};\n\nconst slice = createSlice({\n name: 'convertDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction<boolean>) => {\n state.visible = action.payload;\n },\n setFormat: (state, action: PayloadAction<string>) => {\n state.format = action.payload;\n },\n },\n});\n\nexport const { actions, reducer } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface DumpDialogState {\n visible: boolean;\n inputDeviceId: string;\n}\n\nconst initialState: DumpDialogState = {\n visible: false,\n inputDeviceId: '',\n};\n\nexport const slice = createSlice({\n name: 'dumpDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction<boolean>) => {\n state.visible = action.payload;\n },\n setInputDeviceId: (state, action: PayloadAction<string>) => {\n state.inputDeviceId = action.payload;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface RecordingDialogState {\n visible: boolean;\n\n trackTotal: number;\n trackDone: number;\n trackCurrent: number;\n\n titleCurrent: string;\n}\n\nconst initialState: RecordingDialogState = {\n visible: false,\n\n trackTotal: 1,\n trackDone: 0,\n trackCurrent: 0,\n\n titleCurrent: '',\n\n // visible: true,\n // trackTotal: 4,\n // trackDone: 1,\n // trackCurrent: 25,\n\n // titleCurrent: 'Seconda traccia',\n};\n\nexport const slice = createSlice({\n name: 'recordDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction<boolean>) => {\n state.visible = action.payload;\n },\n setProgress: (\n state,\n action: PayloadAction<{ trackTotal: number; trackDone: number; trackCurrent: number; titleCurrent: string }>\n ) => {\n state.trackTotal = action.payload.trackTotal;\n state.trackDone = action.payload.trackDone;\n state.trackCurrent = action.payload.trackCurrent;\n state.titleCurrent = action.payload.titleCurrent;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\nimport { savePreference, loadPreference } from '../utils';\n\ntype Views = 'WELCOME' | 'MAIN';\n\nexport interface AppState {\n mainView: Views;\n loading: boolean;\n pairingFailed: boolean;\n pairingMessage: string;\n browserSupported: boolean;\n darkMode: boolean;\n aboutDialogVisible: boolean;\n}\n\nconst initialState: AppState = {\n mainView: 'WELCOME',\n loading: false,\n pairingFailed: false,\n pairingMessage: ``,\n browserSupported: true,\n darkMode: loadPreference('darkMode', false),\n aboutDialogVisible: false,\n};\n\nexport const slice = createSlice({\n name: 'app',\n initialState,\n reducers: {\n setState: (state, action: PayloadAction<Views>) => {\n state.mainView = action.payload;\n },\n setLoading: (state, action: PayloadAction<boolean>) => {\n state.loading = action.payload;\n },\n setPairingFailed: (state, action: PayloadAction<boolean>) => {\n state.pairingFailed = action.payload;\n },\n setPairingMessage: (state, action: PayloadAction<string>) => {\n state.pairingMessage = action.payload;\n },\n setBrowserSupported: (state, action: PayloadAction<boolean>) => {\n state.browserSupported = action.payload;\n },\n setDarkMode: (state, action: PayloadAction<boolean>) => {\n state.darkMode = action.payload;\n savePreference('darkMode', state.darkMode);\n },\n showAboutDialog: (state, action: PayloadAction<boolean>) => {\n state.aboutDialogVisible = action.payload;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { Disc } from 'netmd-js';\nimport { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface MainState {\n disc: Disc | null;\n deviceName: string;\n}\n\nconst initialState: MainState = {\n disc: null,\n deviceName: '',\n};\n\nexport const slice = createSlice({\n name: 'main',\n initialState,\n reducers: {\n setDisc: (state, action: PayloadAction<Disc>) => {\n state.disc = action.payload;\n },\n setDeviceName: (state, action: PayloadAction<string>) => {\n state.deviceName = action.payload;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';\nimport uploadDialog from './upload-dialog-feature';\nimport renameDialog from './rename-dialog-feature';\nimport errorDialog from './error-dialog-feature';\nimport convertDialog from './convert-dialog-feature';\nimport dumpDialog from './dump-dialog-feature';\nimport recordDialog from './record-dialog-feature';\nimport appState from './app-feature';\nimport main from './main-feature';\n\nexport const store = configureStore({\n reducer: {\n renameDialog,\n uploadDialog,\n errorDialog,\n convertDialog,\n dumpDialog,\n recordDialog,\n appState,\n main,\n },\n middleware: [...getDefaultMiddleware()],\n});\n\nexport type RootState = ReturnType<typeof store.getState>;\nexport type AppDispatch = typeof store.dispatch;\n","import { batchActions } from 'redux-batched-actions';\nimport { AppDispatch, RootState } from './store';\nimport { actions as uploadDialogActions } from './upload-dialog-feature';\nimport { actions as renameDialogActions } from './rename-dialog-feature';\nimport { actions as errorDialogAction } from './error-dialog-feature';\nimport { actions as recordDialogAction } from './record-dialog-feature';\nimport { actions as appStateActions } from './app-feature';\nimport { actions as mainActions } from './main-feature';\nimport serviceRegistry from '../services/registry';\nimport { Wireformat, getTracks } from 'netmd-js';\nimport { AnyAction } from '@reduxjs/toolkit';\nimport { getAvailableCharsForTrackTitle, framesToSec, sleepWithProgressCallback, sleep } from '../utils';\n\nexport function pair() {\n return async function(dispatch: AppDispatch, getState: () => RootState) {\n dispatch(appStateActions.setPairingFailed(false));\n\n await serviceRegistry.audioExportService!.init();\n\n try {\n let connected = await serviceRegistry.netmdService!.connect();\n if (connected) {\n dispatch(appStateActions.setState('MAIN'));\n return;\n }\n } catch (err) {\n console.error(err);\n // In case of error, just log and try to pair\n }\n\n try {\n let paired = await serviceRegistry.netmdService!.pair();\n if (paired) {\n dispatch(appStateActions.setState('MAIN'));\n return;\n }\n dispatch(batchActions([appStateActions.setPairingMessage(`Connection Failed`), appStateActions.setPairingFailed(true)]));\n } catch (err) {\n console.error(err);\n let message = (err as Error).message;\n dispatch(batchActions([appStateActions.setPairingMessage(message), appStateActions.setPairingFailed(true)]));\n }\n };\n}\n\nexport function listContent() {\n return async function(dispatch: AppDispatch) {\n // Issue loading\n dispatch(appStateActions.setLoading(true));\n let disc = await serviceRegistry.netmdService!.listContent();\n let deviceName = await serviceRegistry.netmdService!.getDeviceName();\n dispatch(batchActions([mainActions.setDisc(disc), mainActions.setDeviceName(deviceName), appStateActions.setLoading(false)]));\n };\n}\n\nexport function renameTrack({ index, newName }: { index: number; newName: string }) {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n dispatch(renameDialogActions.setVisible(false));\n try {\n await netmdService!.renameTrack(index, newName);\n } catch (err) {\n console.error(err);\n dispatch(batchActions([errorDialogAction.setVisible(true), errorDialogAction.setErrorMessage(`Rename failed.`)]));\n }\n listContent()(dispatch);\n };\n}\n\nexport function renameDisc({ newName }: { newName: string }) {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n await netmdService!.renameDisc(newName);\n dispatch(renameDialogActions.setVisible(false));\n listContent()(dispatch);\n };\n}\n\nexport function deleteTracks(indexes: number[]) {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n dispatch(appStateActions.setLoading(true));\n indexes = indexes.sort();\n indexes.reverse();\n for (let index of indexes) {\n await netmdService!.deleteTrack(index);\n }\n listContent()(dispatch);\n };\n}\n\nexport function wipeDisc() {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n dispatch(appStateActions.setLoading(true));\n await netmdService!.wipeDisc();\n listContent()(dispatch);\n };\n}\n\nexport function moveTrack(srcIndex: number, destIndex: number) {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n await netmdService!.moveTrack(srcIndex, destIndex);\n listContent()(dispatch);\n };\n}\n\nexport function recordTracks(indexes: number[], deviceId: string) {\n return async function(dispatch: AppDispatch, getState: () => RootState) {\n dispatch(\n batchActions([\n recordDialogAction.setVisible(true),\n recordDialogAction.setProgress({ trackTotal: indexes.length, trackDone: 0, trackCurrent: 0, titleCurrent: '' }),\n ])\n );\n\n let disc = getState().main.disc;\n let tracks = getTracks(disc!).filter(t => indexes.indexOf(t.index) >= 0);\n\n const { netmdService, mediaRecorderService } = serviceRegistry;\n\n for (let [i, track] of tracks.entries()) {\n dispatch(\n recordDialogAction.setProgress({\n trackTotal: tracks.length,\n trackDone: i,\n trackCurrent: -1,\n titleCurrent: track.title ?? '',\n })\n );\n\n // Wait for the track to be ready to play from 0:00\n await netmdService!.gotoTrack(track.index);\n await netmdService!.play();\n console.log('Waiting for track to be ready to play');\n let position = await netmdService!.getPosition();\n let expected = [track.index, 0, 0, 1];\n while (position === null || !expected.every((_, i) => expected[i] === position![i])) {\n await sleep(250);\n position = await netmdService!.getPosition();\n }\n await netmdService!.pause();\n await netmdService?.gotoTrack(track.index);\n console.log('Track is ready to play');\n\n // Start recording and play track\n await mediaRecorderService?.initStream(deviceId);\n await mediaRecorderService?.startRecording();\n await netmdService!.play();\n\n // Wait until track is finished\n let durationInSec = framesToSec(track.duration);\n // await sleep(durationInSec * 1000);\n await sleepWithProgressCallback(durationInSec * 1000, (perc: number) => {\n dispatch(\n recordDialogAction.setProgress({\n trackTotal: tracks.length,\n trackDone: i,\n trackCurrent: perc,\n titleCurrent: track.title ?? '',\n })\n );\n });\n\n // Stop recording and download the wav\n await mediaRecorderService?.stopRecording();\n mediaRecorderService?.downloadRecorded(`${track.title}`);\n\n await mediaRecorderService?.closeStream();\n }\n\n await netmdService!.stop();\n dispatch(recordDialogAction.setVisible(false));\n };\n}\n\nexport const WireformatDict: { [k: string]: Wireformat } = {\n SP: Wireformat.pcm,\n LP2: Wireformat.lp2,\n LP105: Wireformat.l105kbps,\n LP4: Wireformat.lp4,\n};\n\nexport function convertAndUpload(files: File[], format: string) {\n return async function(dispatch: AppDispatch, getState: () => RootState) {\n const { audioExportService, netmdService } = serviceRegistry;\n const wireformat = WireformatDict[format];\n\n dispatch(uploadDialogActions.setVisible(true));\n\n const updateProgressCallback = ({ written, encrypted, total }: { written: number; encrypted: number; total: number }) => {\n dispatch(uploadDialogActions.setWriteProgress({ written, encrypted, total }));\n };\n\n let trackUpdate: {\n current: number;\n converting: number;\n total: number;\n titleCurrent: string;\n titleConverting: string;\n } = {\n current: 0,\n converting: 0,\n total: files.length,\n titleCurrent: '',\n titleConverting: '',\n };\n const updateTrack = () => {\n dispatch(uploadDialogActions.setTrackProgress(trackUpdate));\n };\n\n let conversionIterator = async function*(files: File[]) {\n let converted: Promise<{ file: File; data: ArrayBuffer }>[] = [];\n\n let i = 0;\n function convertNext() {\n if (i === files.length) {\n trackUpdate.converting = i;\n trackUpdate.titleConverting = ``;\n updateTrack();\n return;\n }\n\n let f = files[i];\n trackUpdate.converting = i;\n trackUpdate.titleConverting = f.name;\n updateTrack();\n i++;\n\n converted.push(\n new Promise(async (resolve, reject) => {\n let data: ArrayBuffer;\n try {\n await audioExportService!.prepare(f);\n data = await audioExportService!.export({ format });\n convertNext();\n resolve({ file: f, data: data });\n } catch (err) {\n error = err;\n errorMessage = `${f.name}: Unsupported or unrecognized format`;\n reject(err);\n }\n })\n );\n }\n convertNext();\n\n let j = 0;\n while (j < converted.length) {\n yield await converted[j];\n delete converted[j];\n j++;\n }\n };\n\n let disc = getState().main.disc;\n let maxTitleLength = disc ? getAvailableCharsForTrackTitle(getTracks(disc).map(track => track.title || ``)) : -1;\n maxTitleLength = Math.floor(maxTitleLength / files.length);\n\n let error: any;\n let errorMessage = ``;\n let i = 1;\n for await (let item of conversionIterator(files)) {\n const { file, data } = item;\n\n let title = file.name;\n const extStartIndex = title.lastIndexOf('.');\n if (extStartIndex > 0) {\n title = title.substring(0, extStartIndex);\n }\n if (maxTitleLength > -1) {\n title = title.substring(0, maxTitleLength);\n }\n\n trackUpdate.current = i++;\n trackUpdate.titleCurrent = title;\n updateTrack();\n updateProgressCallback({ written: 0, encrypted: 0, total: 100 });\n try {\n await netmdService?.upload(title, data, wireformat, updateProgressCallback);\n } catch (err) {\n error = err;\n errorMessage = `${file.name}: Error uploading to device`;\n break;\n }\n }\n\n let actionToDispatch: AnyAction[] = [uploadDialogActions.setVisible(false)];\n\n if (error) {\n console.error(error);\n actionToDispatch = actionToDispatch.concat([\n errorDialogAction.setVisible(true),\n errorDialogAction.setErrorMessage(errorMessage),\n ]);\n }\n\n dispatch(batchActions(actionToDispatch));\n listContent()(dispatch);\n };\n}\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\n\nimport { actions as appActions } from '../redux/app-feature';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogContentText from '@material-ui/core/DialogContentText';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\nimport Link from '@material-ui/core/Link';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return <Slide direction=\"up\" ref={ref} {...props} />;\n});\n\nexport const AboutDialog = (props: {}) => {\n const dispatch = useDispatch();\n\n let visible = useShallowEqualSelector(state => state.appState.aboutDialogVisible);\n\n const handleClose = () => {\n dispatch(appActions.showAboutDialog(false));\n };\n\n return (\n <Dialog\n open={visible}\n maxWidth={'sm'}\n fullWidth={true}\n TransitionComponent={Transition as any}\n aria-labelledby=\"about-dialog-slide-title\"\n >\n <DialogTitle id=\"about-dialog-slide-title\">About Web MiniDisc</DialogTitle>\n <DialogContent>\n <DialogContentText>Web MiniDisc has been made possible by</DialogContentText>\n <ul>\n <li>\n <Link rel=\"noopener noreferrer\" href=\"https://www.ffmpeg.org/\" target=\"_blank\">\n FFmpeg\n </Link>{' '}\n and{' '}\n <Link rel=\"noopener noreferrer\" href=\"https://github.com/ffmpegjs/FFmpeg\" target=\"_blank\">\n ffmpegjs\n </Link>\n , to read your audio files (wav, mp3, ogg, mp4, etc...).\n </li>\n <li>\n <Link rel=\"noopener noreferrer\" href=\"https://github.com/dcherednik/atracdenc/\" target=\"_blank\">\n Atracdenc\n </Link>\n , to support atrac3 encoding (lp2, lp4 audio formats).\n </li>\n <li>\n <Link rel=\"noopener noreferrer\" href=\"https://emscripten.org/\" target=\"_blank\">\n Emscripten\n </Link>\n , to run both FFmpeg and Atracdenc in the browser.\n </li>\n <li>\n <Link rel=\"noopener noreferrer\" href=\"https://github.com/cybercase/netmd-js\" target=\"_blank\">\n netmd-js\n </Link>\n , to send commands to NetMD devices using Javascript.\n </li>\n <li>\n <Link rel=\"noopener noreferrer\" href=\"https://github.com/glaubitz/linux-minidisc\" target=\"_blank\">\n linux-minidisc\n </Link>\n , to make the netmd-js project possible.\n </li>\n <li>\n <Link rel=\"noopener noreferrer\" href=\"https://material-ui.com/\" target=\"_blank\">\n material-ui\n </Link>\n , to build the user interface.\n </li>\n </ul>\n <DialogContentText>Attribution</DialogContentText>\n <ul>\n <li>\n MiniDisc logo from{' '}\n <Link rel=\"noopener noreferrer\" href=\"https://en.wikipedia.org/wiki/MiniDisc\" target=\"_blank\">\n https://en.wikipedia.org/wiki/MiniDisc\n </Link>\n </li>\n <li>\n MiniDisc icon from{' '}\n <Link\n rel=\"noopener noreferrer\"\n href=\"https://www.deviantart.com/blinkybill/art/Sony-MiniDisc-Plastic-Icon-473812540\"\n target=\"_blank\"\n >\n http://fav.me/d7u3g3g\n </Link>\n </li>\n </ul>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose}>Close</Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { batchActions } from 'redux-batched-actions';\n\nimport IconButton from '@material-ui/core/IconButton';\nimport Menu from '@material-ui/core/Menu';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport MoreVertIcon from '@material-ui/icons/MoreVert';\n\nimport { wipeDisc, listContent } from '../redux/actions';\nimport { actions as appActions } from '../redux/app-feature';\nimport { actions as renameDialogActions } from '../redux/rename-dialog-feature';\nimport { useShallowEqualSelector } from '../utils';\nimport Link from '@material-ui/core/Link';\n\nexport const TopMenu = function() {\n const dispatch = useDispatch();\n\n let { mainView } = useShallowEqualSelector(state => state.appState);\n let disc = useShallowEqualSelector(state => state.main.disc);\n\n const [menuAnchorEl, setMenuAnchorEl] = React.useState<null | HTMLElement>(null);\n const menuOpen = Boolean(menuAnchorEl);\n const handleMenuClick = (event: React.MouseEvent<HTMLElement>) => {\n setMenuAnchorEl(event.currentTarget);\n };\n\n const handleMenuClose = () => {\n setMenuAnchorEl(null);\n };\n\n const handleWipeDisc = () => {\n dispatch(wipeDisc());\n handleMenuClose();\n };\n\n const handleRefresh = () => {\n dispatch(listContent());\n handleMenuClose();\n };\n\n const handleRenameDisc = () => {\n dispatch(\n batchActions([\n renameDialogActions.setVisible(true),\n renameDialogActions.setCurrentName(disc?.title ?? ``),\n renameDialogActions.setIndex(-1),\n ])\n );\n handleMenuClose();\n };\n\n const handleExit = () => {\n dispatch(appActions.setState('WELCOME'));\n handleMenuClose();\n };\n const handleShowAbout = () => {\n dispatch(appActions.showAboutDialog(true));\n handleMenuClose();\n };\n\n const menuItems = [];\n if (mainView === 'MAIN') {\n menuItems.push(\n <MenuItem key=\"update\" onClick={handleRefresh}>\n Refresh\n </MenuItem>\n );\n menuItems.push(\n <MenuItem key=\"title\" onClick={handleRenameDisc}>\n Rename Disc\n </MenuItem>\n );\n menuItems.push(\n <MenuItem key=\"wipe\" onClick={handleWipeDisc}>\n Wipe Disc\n </MenuItem>\n );\n menuItems.push(\n <MenuItem key=\"exit\" onClick={handleExit}>\n Exit\n </MenuItem>\n );\n }\n menuItems.push(\n <MenuItem key=\"about\" onClick={handleShowAbout}>\n About\n </MenuItem>\n );\n menuItems.push(\n <MenuItem key=\"github\" onClick={handleMenuClose}>\n <Link rel=\"noopener noreferrer\" href=\"https://github.com/cybercase/webminidisc\" target=\"_blank\">\n Fork me on GitHub\n </Link>\n </MenuItem>\n );\n\n return (\n <React.Fragment>\n <IconButton aria-label=\"actions\" aria-controls=\"actions-menu\" aria-haspopup=\"true\" onClick={handleMenuClick}>\n <MoreVertIcon />\n </IconButton>\n <Menu id=\"actions-menu\" anchorEl={menuAnchorEl} keepMounted open={menuOpen} onClose={handleMenuClose}>\n {menuItems}\n </Menu>\n </React.Fragment>\n );\n};\n","import React, { useState } from 'react';\nimport { useDispatch } from 'react-redux';\nimport { pair } from '../redux/actions';\n\nimport { useShallowEqualSelector } from '../utils';\n\nimport { makeStyles } from '@material-ui/core/styles';\nimport Button from '@material-ui/core/Button';\nimport Typography from '@material-ui/core/Typography';\nimport FormControl from '@material-ui/core/FormControl';\nimport FormHelperText from '@material-ui/core/FormHelperText';\nimport Box from '@material-ui/core/Box';\nimport Link from '@material-ui/core/Link';\n\nimport { AboutDialog } from './about-dialog';\nimport { TopMenu } from './topmenu';\nimport ChromeIconPath from '../images/chrome-icon.svg';\n\nconst useStyles = makeStyles(theme => ({\n main: {\n position: 'relative',\n flex: '1 1 auto',\n display: 'flex',\n justifyContent: 'center',\n flexDirection: 'column',\n alignItems: 'center',\n },\n button: {\n marginTop: theme.spacing(3),\n minWidth: 150,\n },\n spacing: {\n marginTop: theme.spacing(1),\n },\n chromeLogo: {\n marginTop: theme.spacing(1),\n width: 96,\n height: 96,\n },\n why: {\n alignSelf: 'flex-start',\n marginTop: theme.spacing(3),\n },\n headBox: {\n display: 'flex',\n justifyContent: 'space-between',\n },\n}));\n\nexport const Welcome = (props: {}) => {\n const classes = useStyles();\n\n const dispatch = useDispatch();\n const { browserSupported, pairingFailed, pairingMessage } = useShallowEqualSelector(state => state.appState);\n if (pairingMessage.toLowerCase().match(/denied/)) {\n // show linux instructions\n }\n // Access denied.\n\n const [showWhyUnsupported, setWhyUnsupported] = useState(false);\n const handleLearnWhy = (event: React.SyntheticEvent) => {\n event.preventDefault();\n setWhyUnsupported(true);\n };\n\n return (\n <React.Fragment>\n <Box className={classes.headBox}>\n <Typography component=\"h1\" variant=\"h4\">\n Web MiniDisc\n </Typography>\n <TopMenu />\n </Box>\n <Typography component=\"h2\" variant=\"body2\">\n Brings NetMD Devices to the Web\n </Typography>\n <Box className={classes.main}>\n {browserSupported ? (\n <React.Fragment>\n <Typography component=\"h2\" variant=\"subtitle1\" align=\"center\" className={classes.spacing}>\n Press the button to connect to a NetMD device\n </Typography>\n\n <Button variant=\"contained\" color=\"primary\" onClick={() => dispatch(pair())} className={classes.button}>\n Connect\n </Button>\n\n <FormControl error={true} className={classes.spacing} style={{ visibility: pairingFailed ? 'visible' : 'hidden' }}>\n <FormHelperText>{pairingMessage}</FormHelperText>\n </FormControl>\n </React.Fragment>\n ) : (\n <React.Fragment>\n <Typography component=\"h2\" variant=\"subtitle1\" align=\"center\" className={classes.spacing}>\n This Web browser is not supported.&nbsp;\n <Link rel=\"noopener noreferrer\" href=\"#\" onClick={handleLearnWhy}>\n Learn Why\n </Link>\n </Typography>\n\n <Link rel=\"noopener noreferrer\" target=\"_blank\" href=\"https://www.google.com/chrome/\">\n <img alt=\"Chrome Logo\" src={ChromeIconPath} className={classes.chromeLogo} />\n </Link>\n\n <Typography component=\"h2\" variant=\"subtitle1\" align=\"center\" className={classes.spacing}>\n Try using{' '}\n <Link rel=\"noopener noreferrer\" target=\"_blank\" href=\"https://www.google.com/chrome/\">\n Chrome\n </Link>{' '}\n instead\n </Typography>\n\n {showWhyUnsupported ? (\n <>\n <Typography component=\"p\" variant=\"body2\" className={classes.why}>\n Web MiniDisc requires a browser that supports both{' '}\n <Link rel=\"noopener noreferrer\" target=\"_blank\" href=\"https://wicg.github.io/webusb/\">\n WebUSB\n </Link>{' '}\n and{' '}\n <Link rel=\"noopener noreferrer\" target=\"_blank\" href=\"https://webassembly.org/\">\n WebAssembly\n </Link>\n .\n </Typography>\n <ul>\n <li>WebUSB is needed to control the NetMD device via the USB connection to your computer.</li>\n <li>WebAssembly is used to convert the music to a MiniDisc compatible format</li>\n </ul>\n </>\n ) : null}\n </React.Fragment>\n )}\n </Box>\n <AboutDialog />\n </React.Fragment>\n );\n};\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\nimport { actions as renameDialogActions } from '../redux/rename-dialog-feature';\nimport { renameTrack, renameDisc } from '../redux/actions';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport TextField from '@material-ui/core/TextField';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return <Slide direction=\"up\" ref={ref} {...props} />;\n});\n\nexport const RenameDialog = (props: {}) => {\n let dispatch = useDispatch();\n\n let renameDialogVisible = useShallowEqualSelector(state => state.renameDialog.visible);\n let renameDialogTitle = useShallowEqualSelector(state => state.renameDialog.title);\n let renameDialogIndex = useShallowEqualSelector(state => state.renameDialog.index);\n\n const what = renameDialogIndex < 0 ? `Disc` : `Track`;\n\n const handleCancelRename = () => {\n dispatch(renameDialogActions.setVisible(false));\n };\n\n const handleDoRename = () => {\n if (renameDialogIndex < 0) {\n dispatch(renameDisc({ newName: renameDialogTitle }));\n } else {\n dispatch(renameTrack({ index: renameDialogIndex, newName: renameDialogTitle }));\n }\n };\n\n return (\n <Dialog\n open={renameDialogVisible}\n onClose={handleCancelRename}\n maxWidth={'sm'}\n fullWidth={true}\n TransitionComponent={Transition as any}\n aria-labelledby=\"rename-dialog-title\"\n >\n <DialogTitle id=\"rename-dialog-title\">Rename {what}</DialogTitle>\n <DialogContent>\n <TextField\n autoFocus\n id=\"name\"\n label={`${what} Name`}\n type=\"text\"\n fullWidth\n value={renameDialogTitle}\n onKeyDown={event => {\n event.key === `Enter` && handleDoRename();\n }}\n onChange={event => {\n dispatch(renameDialogActions.setCurrentName(event.target.value.substring(0, 120))); // MAX title length\n }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCancelRename}>Cancel</Button>\n <Button color={'primary'} onClick={handleDoRename}>\n Rename\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React from 'react';\nimport { useShallowEqualSelector } from '../utils';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogContentText from '@material-ui/core/DialogContentText';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport LinearProgress from '@material-ui/core/LinearProgress';\nimport Box from '@material-ui/core/Box';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles(theme => ({\n progressPerc: {\n marginTop: theme.spacing(1),\n },\n progressBar: {\n marginTop: theme.spacing(3),\n },\n uploadLabel: {\n marginTop: theme.spacing(3),\n },\n}));\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return <Slide direction=\"up\" ref={ref} {...props} />;\n});\n\nexport const UploadDialog = (props: {}) => {\n const classes = useStyles();\n\n let {\n visible,\n writtenProgress,\n encryptedProgress,\n totalProgress,\n\n trackTotal,\n trackCurrent,\n trackConverting,\n titleCurrent,\n titleConverting,\n } = useShallowEqualSelector(state => state.uploadDialog);\n\n let progressValue = Math.floor((writtenProgress / totalProgress) * 100);\n let bufferValue = Math.floor((encryptedProgress / totalProgress) * 100);\n let convertedValue = Math.floor((trackConverting / trackTotal) * 100);\n return (\n <Dialog\n open={visible}\n maxWidth={'sm'}\n fullWidth={true}\n TransitionComponent={Transition as any}\n aria-labelledby=\"alert-dialog-slide-title\"\n aria-describedby=\"alert-dialog-slide-description\"\n >\n <DialogTitle id=\"alert-dialog-slide-title\">Recording...</DialogTitle>\n <DialogContent>\n <DialogContentText id=\"alert-dialog-slide-description\">\n {convertedValue === 100 && trackConverting === trackTotal\n ? `Conversion completed`\n : `Converting ${trackConverting + 1} of ${trackTotal}: ${titleConverting}`}\n </DialogContentText>\n <LinearProgress\n className={classes.progressBar}\n variant={convertedValue === 0 ? 'indeterminate' : 'determinate'}\n color=\"primary\"\n value={convertedValue}\n />\n <Box className={classes.progressPerc}>{convertedValue}%</Box>\n\n <DialogContentText id=\"alert-dialog-slide-description\" className={classes.uploadLabel}>\n Uploading {trackCurrent} of {trackTotal}: {titleCurrent}\n </DialogContentText>\n <LinearProgress\n className={classes.progressBar}\n variant=\"buffer\"\n color=\"secondary\"\n value={progressValue}\n valueBuffer={bufferValue}\n />\n <Box className={classes.progressPerc}>{progressValue}%</Box>\n </DialogContent>\n <DialogActions></DialogActions>\n </Dialog>\n );\n};\n","import React from 'react';\nimport { useShallowEqualSelector } from '../utils';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogContentText from '@material-ui/core/DialogContentText';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport LinearProgress from '@material-ui/core/LinearProgress';\nimport Box from '@material-ui/core/Box';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles(theme => ({\n progressPerc: {\n marginTop: theme.spacing(1),\n },\n progressBar: {\n marginTop: theme.spacing(3),\n },\n}));\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return <Slide direction=\"up\" ref={ref} {...props} />;\n});\n\nexport const RecordDialog = (props: {}) => {\n const classes = useStyles();\n\n let { visible, trackTotal, trackDone, trackCurrent, titleCurrent } = useShallowEqualSelector(state => state.recordDialog);\n\n let progressValue = Math.round(trackCurrent);\n return (\n <Dialog\n open={visible}\n maxWidth={'sm'}\n fullWidth={true}\n TransitionComponent={Transition as any}\n aria-labelledby=\"record-dialog-slide-title\"\n aria-describedby=\"record-dialog-slide-description\"\n >\n <DialogTitle id=\"record-dialog-slide-title\">Recording...</DialogTitle>\n <DialogContent>\n <DialogContentText id=\"record-dialog-slide-description\">\n {`Recording track ${trackDone + 1} of ${trackTotal}: ${titleCurrent}`}\n </DialogContentText>\n <LinearProgress\n className={classes.progressBar}\n variant={trackCurrent >= 0 ? 'determinate' : 'indeterminate'}\n color=\"primary\"\n value={progressValue}\n />\n <Box className={classes.progressPerc}>{progressValue >= 0 ? `${progressValue}%` : ``}</Box>\n </DialogContent>\n <DialogActions></DialogActions>\n </Dialog>\n );\n};\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\n\nimport { actions as errorDialogActions } from '../redux/error-dialog-feature';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogContentText from '@material-ui/core/DialogContentText';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return <Slide direction=\"up\" ref={ref} {...props} />;\n});\n\nexport const ErrorDialog = (props: {}) => {\n const dispatch = useDispatch();\n\n let { visible, error } = useShallowEqualSelector(state => state.errorDialog);\n\n const handleClose = () => {\n dispatch(errorDialogActions.setVisible(false));\n };\n\n return (\n <Dialog\n open={visible}\n maxWidth={'sm'}\n fullWidth={true}\n TransitionComponent={Transition as any}\n aria-labelledby=\"error-dialog-slide-title\"\n aria-describedby=\"error-dialog-slide-description\"\n >\n <DialogTitle id=\"alert-dialog-slide-title\">Error</DialogTitle>\n <DialogContent>\n <DialogContentText id=\"alert-dialog-slide-description\">{error}</DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose}>Close</Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\n\nimport { actions as convertDialogActions } from '../redux/convert-dialog-feature';\nimport { convertAndUpload } from '../redux/actions';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\nimport { makeStyles } from '@material-ui/core/styles';\nimport FormControl from '@material-ui/core/FormControl';\nimport InputLabel from '@material-ui/core/InputLabel';\nimport Select from '@material-ui/core/Select';\nimport Input from '@material-ui/core/Input';\nimport MenuItem from '@material-ui/core/MenuItem';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return <Slide direction=\"up\" ref={ref} {...props} />;\n});\n\nconst useStyles = makeStyles(theme => ({\n container: {\n display: 'flex',\n flexDirection: 'row',\n },\n formControl: {\n minWidth: 120,\n },\n}));\n\nexport const ConvertDialog = (props: { files: File[] }) => {\n const dispatch = useDispatch();\n const classes = useStyles();\n\n let { visible, format } = useShallowEqualSelector(state => state.convertDialog);\n\n const handleClose = () => {\n dispatch(convertDialogActions.setVisible(false));\n };\n\n const handleChange = (ev: React.ChangeEvent<{ value: unknown }>) => {\n dispatch(convertDialogActions.setFormat(ev.target.value as string));\n };\n\n const handleConvert = () => {\n handleClose();\n dispatch(convertAndUpload(props.files, format));\n };\n\n return (\n <Dialog\n open={visible}\n maxWidth={'xs'}\n fullWidth={true}\n TransitionComponent={Transition as any}\n aria-labelledby=\"convert-dialog-slide-title\"\n aria-describedby=\"convert-dialog-slide-description\"\n >\n <DialogTitle id=\"convert-dialog-slide-title\">Upload Settings</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel color=\"secondary\" id=\"convert-dialog-format\">\n Format\n </InputLabel>\n <Select\n labelId=\"convert-dialog-format-label\"\n id=\"convert-dialog-format\"\n value={format}\n color=\"secondary\"\n onChange={handleChange}\n input={<Input />}\n >\n <MenuItem value={`SP`}>SP</MenuItem>\n <MenuItem value={`LP2`}>LP2</MenuItem>\n <MenuItem value={`LP4`}>LP4</MenuItem>\n </Select>\n </FormControl>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose}>Cancel</Button>\n <Button onClick={handleConvert}>Ok</Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useCallback } from 'react';\n\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport StopIcon from '@material-ui/icons/Stop';\nimport SkipNextIcon from '@material-ui/icons/SkipNext';\nimport SkipPreviousIcon from '@material-ui/icons/SkipPrevious';\n\nimport IconButton from '@material-ui/core/IconButton';\nimport Box from '@material-ui/core/Box';\n\nimport serviceRegistry from '../services/registry';\n\nexport const Controls = () => {\n const handlePrev = useCallback(() => {\n serviceRegistry.netmdService?.prev();\n }, []);\n const handlePlay = useCallback(() => {\n serviceRegistry.netmdService?.play();\n }, []);\n const handleStop = useCallback(() => {\n serviceRegistry.netmdService?.stop();\n }, []);\n const handleNext = useCallback(() => {\n serviceRegistry.netmdService?.next();\n }, []);\n return (\n <Box>\n <IconButton aria-label=\"prev\" onClick={handlePrev}>\n <SkipPreviousIcon />\n </IconButton>\n <IconButton aria-label=\"play\" onClick={handlePlay}>\n <PlayArrowIcon />\n </IconButton>\n <IconButton aria-label=\"stop\" onClick={handleStop}>\n <StopIcon />\n </IconButton>\n <IconButton aria-label=\"next\" onClick={handleNext}>\n <SkipNextIcon />\n </IconButton>\n </Box>\n );\n};\n","import React, { useEffect, useState, useCallback } from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\n\nimport { recordTracks } from '../redux/actions';\nimport { actions as dumpDialogActions } from '../redux/dump-dialog-feature';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\nimport { makeStyles } from '@material-ui/core/styles';\nimport FormControl from '@material-ui/core/FormControl';\nimport Select from '@material-ui/core/Select';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport Typography from '@material-ui/core/Typography';\nimport FormHelperText from '@material-ui/core/FormHelperText';\nimport { Controls } from './controls';\nimport Box from '@material-ui/core/Box';\nimport serviceRegistry from '../services/registry';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return <Slide direction=\"up\" ref={ref} {...props} />;\n});\n\nconst useStyles = makeStyles(theme => ({\n container: {\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'flex-end',\n justifyContent: 'space-between',\n marginRight: -theme.spacing(2),\n },\n formControl: {\n minWidth: 120,\n },\n selectEmpty: {\n marginTop: theme.spacing(2),\n },\n head: {\n textShadow: '0px 0px 12px rgba(150, 150, 150, 1)',\n fontSize: theme.typography.h2.fontSize,\n textAlign: 'center',\n marginBottom: theme.spacing(2),\n },\n}));\n\nexport const DumpDialog = ({ trackIndexes }: { trackIndexes: number[] }) => {\n const dispatch = useDispatch();\n const classes = useStyles();\n\n const [devices, setDevices] = useState<{ deviceId: string; label: string }[]>([]);\n const [inputDeviceId, setInputDeviceId] = useState<string>('');\n\n let { visible } = useShallowEqualSelector(state => state.dumpDialog);\n\n const handleClose = useCallback(() => {\n setInputDeviceId('');\n serviceRegistry.mediaRecorderService?.stopTestInput();\n dispatch(dumpDialogActions.setVisible(false));\n }, [dispatch]);\n\n const handleChange = useCallback(\n (ev: React.ChangeEvent<{ value: unknown }>) => {\n const deviceId = ev.target.value as string;\n setInputDeviceId(deviceId);\n serviceRegistry.mediaRecorderService?.stopTestInput();\n serviceRegistry.mediaRecorderService?.playTestInput(deviceId);\n },\n [setInputDeviceId]\n );\n\n const handleStartTransfer = useCallback(() => {\n dispatch(recordTracks(trackIndexes, inputDeviceId));\n handleClose();\n }, [trackIndexes, inputDeviceId, dispatch, handleClose]);\n\n useEffect(() => {\n async function updateDeviceList() {\n await navigator.mediaDevices.getUserMedia({ audio: true });\n let devices = await navigator.mediaDevices.enumerateDevices();\n let inputDevices = devices\n .filter(device => device.kind === 'audioinput')\n .map(device => ({ deviceId: device.deviceId, label: device.label }));\n setDevices(inputDevices);\n }\n if (visible) {\n updateDeviceList();\n }\n }, [visible, setDevices]);\n\n return (\n <Dialog\n open={visible}\n maxWidth={'sm'}\n fullWidth={true}\n TransitionComponent={Transition as any}\n aria-labelledby=\"dump-dialog-slide-title\"\n aria-describedby=\"dump-dialog-slide-description\"\n >\n <DialogTitle id=\"dump-dialog-slide-title\">Record Selected Tracks</DialogTitle>\n <DialogContent>\n <Typography component=\"p\" variant=\"h2\" className={classes.head}>\n {`💻 ⬅ 💽`}\n </Typography>\n <Typography component=\"p\" variant=\"body2\">\n 1. Connect your MD Player line-out to your PC audio line-in.\n </Typography>\n <Typography component=\"p\" variant=\"body2\">\n 2. Use the controls at the bottom right to play some tracks.\n </Typography>\n <Typography component=\"p\" variant=\"body2\">\n 3. Select the input source. You should hear the tracks playing on your PC.\n </Typography>\n <Typography component=\"p\" variant=\"body2\">\n 4. Adjust the input gain and the line-out volume of your device.\n </Typography>\n <Box className={classes.container}>\n <FormControl className={classes.formControl}>\n <Select value={inputDeviceId} onChange={handleChange} displayEmpty className={classes.selectEmpty}>\n <MenuItem value=\"\" disabled>\n Input Source\n </MenuItem>\n {devices.map(device => (\n <MenuItem key={device.deviceId} value={device.deviceId}>\n {device.label}\n </MenuItem>\n ))}\n </Select>\n <FormHelperText>Input Source</FormHelperText>\n </FormControl>\n <Controls />\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose}>Cancel</Button>\n <Button onClick={handleStartTransfer} disabled={inputDeviceId === ''}>\n Start Record\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useEffect, useCallback } from 'react';\nimport { useDispatch } from 'react-redux';\nimport clsx from 'clsx';\nimport { useDropzone } from 'react-dropzone';\nimport { listContent, deleteTracks, moveTrack } from '../redux/actions';\nimport { actions as renameDialogActions } from '../redux/rename-dialog-feature';\nimport { actions as convertDialogActions } from '../redux/convert-dialog-feature';\nimport { actions as dumpDialogActions } from '../redux/dump-dialog-feature';\n\nimport { formatTimeFromFrames, getTracks, Encoding } from 'netmd-js';\n\nimport { useShallowEqualSelector } from '../utils';\n\nimport { lighten, makeStyles } from '@material-ui/core/styles';\nimport Typography from '@material-ui/core/Typography';\nimport Box from '@material-ui/core/Box';\nimport Fab from '@material-ui/core/Fab';\nimport AddIcon from '@material-ui/icons/Add';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport EditIcon from '@material-ui/icons/Edit';\nimport Backdrop from '@material-ui/core/Backdrop';\n\nimport Table from '@material-ui/core/Table';\nimport TableBody from '@material-ui/core/TableBody';\nimport TableCell from '@material-ui/core/TableCell';\nimport TableHead from '@material-ui/core/TableHead';\nimport TableRow from '@material-ui/core/TableRow';\n\nimport IconButton from '@material-ui/core/IconButton';\nimport Toolbar from '@material-ui/core/Toolbar';\nimport Tooltip from '@material-ui/core/Tooltip';\nimport { batchActions } from 'redux-batched-actions';\n\nimport { RenameDialog } from './rename-dialog';\nimport { UploadDialog } from './upload-dialog';\nimport { RecordDialog } from './record-dialog';\nimport { ErrorDialog } from './error-dialog';\nimport { ConvertDialog } from './convert-dialog';\nimport { AboutDialog } from './about-dialog';\nimport { DumpDialog } from './dump-dialog';\nimport { TopMenu } from './topmenu';\nimport Checkbox from '@material-ui/core/Checkbox';\nimport * as BadgeImpl from '@material-ui/core/Badge/Badge';\nimport Button from '@material-ui/core/Button';\nimport Menu from '@material-ui/core/Menu';\nimport MenuItem from '@material-ui/core/MenuItem';\n\nconst useStyles = makeStyles(theme => ({\n add: {\n position: 'absolute',\n bottom: theme.spacing(3),\n right: theme.spacing(3),\n },\n main: {\n overflowY: 'auto',\n flex: '1 1 auto',\n marginBottom: theme.spacing(3),\n marginLeft: theme.spacing(-2),\n marginRight: theme.spacing(-2),\n outline: 'none',\n },\n toolbar: {\n marginTop: theme.spacing(3),\n marginLeft: theme.spacing(-2),\n marginRight: theme.spacing(-2),\n [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {\n marginLeft: theme.spacing(-3),\n marginRight: theme.spacing(-3),\n },\n },\n toolbarLabel: {\n flex: '1 1 100%',\n },\n toolbarHighlight:\n theme.palette.type === 'light'\n ? {\n color: theme.palette.secondary.main,\n backgroundColor: lighten(theme.palette.secondary.light, 0.85),\n }\n : {\n color: theme.palette.text.primary,\n backgroundColor: theme.palette.secondary.dark,\n },\n headBox: {\n display: 'flex',\n justifyContent: 'space-between',\n },\n spacing: {\n marginTop: theme.spacing(1),\n },\n formatBadge: {\n ...(BadgeImpl as any).styles(theme).badge,\n ...(BadgeImpl as any).styles(theme).colorPrimary,\n position: 'static',\n display: 'inline-flex',\n border: `2px solid ${theme.palette.background.paper}`,\n padding: '0 4px',\n },\n titleCell: {\n overflow: 'hidden',\n maxWidth: '40ch',\n textOverflow: 'ellipsis',\n // whiteSpace: 'nowrap',\n },\n indexCell: {\n whiteSpace: 'nowrap',\n paddingRight: 0,\n width: `2ch`,\n },\n backdrop: {\n zIndex: theme.zIndex.drawer + 1,\n color: '#fff',\n },\n}));\n\nconst EncodingName: { [k: number]: string } = {\n [Encoding.sp]: 'SP',\n [Encoding.lp2]: 'LP2',\n [Encoding.lp4]: 'LP4',\n};\n\nexport const Main = (props: {}) => {\n let dispatch = useDispatch();\n let disc = useShallowEqualSelector(state => state.main.disc);\n let deviceName = useShallowEqualSelector(state => state.main.deviceName);\n\n const [selected, setSelected] = React.useState<number[]>([]);\n const selectedCount = selected.length;\n\n const [moveMenuAnchorEl, setMoveMenuAnchorEl] = React.useState<null | HTMLElement>(null);\n const handleShowMoveMenu = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {\n setMoveMenuAnchorEl(event.currentTarget);\n }, []);\n const handleCloseMoveMenu = useCallback(() => {\n setMoveMenuAnchorEl(null);\n }, []);\n const handleMoveSelectedTrack = useCallback(\n (destIndex: number) => {\n dispatch(moveTrack(selected[0], destIndex));\n handleCloseMoveMenu();\n },\n [dispatch, selected, handleCloseMoveMenu]\n );\n\n const handleShowDumpDialog = useCallback(() => {\n dispatch(dumpDialogActions.setVisible(true));\n }, [dispatch]);\n\n useEffect(() => {\n dispatch(listContent());\n }, [dispatch]);\n\n useEffect(() => {\n setSelected([]); // Reset selection if disc changes\n }, [disc]);\n\n let [uploadedFiles, setUploadedFiles] = React.useState<File[]>([]);\n const onDrop = useCallback(\n (acceptedFiles: File[], rejectedFiles: File[]) => {\n setUploadedFiles(acceptedFiles);\n dispatch(convertDialogActions.setVisible(true));\n },\n [dispatch]\n );\n const { getRootProps, getInputProps, isDragActive, open } = useDropzone({ onDrop, accept: `audio/*`, noClick: true });\n\n const classes = useStyles();\n\n let tracks: { index: number; title: string; group: string; duration: string; encoding: string }[] = [];\n if (disc !== null) {\n for (let group of disc.groups) {\n for (let track of group.tracks) {\n tracks.push({\n index: track.index,\n title: track.title ?? `Unknown Title`,\n group: group.title ?? ``,\n encoding: EncodingName[track.encoding],\n duration: formatTimeFromFrames(track.duration, false),\n });\n }\n }\n }\n tracks.sort((l, r) => l.index - r.index);\n\n // Action Handlers\n const handleSelectClick = (event: React.MouseEvent, item: number) => {\n if (selected.includes(item)) {\n setSelected(selected.filter(i => i !== item));\n } else {\n setSelected([...selected, item]);\n }\n };\n\n const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => {\n if (selected.length < tracks.length) {\n setSelected(tracks.map(t => t.index));\n } else {\n setSelected([]);\n }\n };\n\n const handleRenameDoubleClick = (event: React.MouseEvent, item: number) => {\n let selectedIndex = item;\n let currentName = getTracks(disc!).find(track => track.index === selectedIndex)?.title ?? '';\n\n dispatch(\n batchActions([\n renameDialogActions.setVisible(true),\n renameDialogActions.setCurrentName(currentName),\n renameDialogActions.setIndex(selectedIndex),\n ])\n );\n };\n\n const handleRenameActionClick = (event: React.MouseEvent) => {\n handleRenameDoubleClick(event, selected[0]);\n };\n\n const handleDeleteSelected = (event: React.MouseEvent) => {\n dispatch(deleteTracks(selected));\n };\n\n return (\n <React.Fragment>\n <Box className={classes.headBox}>\n <Typography component=\"h1\" variant=\"h4\">\n {deviceName || `Loading...`}\n </Typography>\n <TopMenu />\n </Box>\n <Typography component=\"h2\" variant=\"body2\">\n {disc !== null\n ? `${formatTimeFromFrames(disc.left, false)} left of ${formatTimeFromFrames(disc.total, false)}`\n : `Loading...`}\n </Typography>\n <Toolbar\n className={clsx(classes.toolbar, {\n [classes.toolbarHighlight]: selectedCount > 0,\n })}\n >\n {selectedCount > 0 ? (\n <Checkbox\n indeterminate={selectedCount > 0 && selectedCount < tracks.length}\n checked={selectedCount > 0}\n onChange={handleSelectAllClick}\n inputProps={{ 'aria-label': 'select all tracks' }}\n />\n ) : null}\n {selectedCount > 0 ? (\n <Typography className={classes.toolbarLabel} color=\"inherit\" variant=\"subtitle1\">\n {selectedCount} selected\n </Typography>\n ) : (\n <Typography component=\"h3\" variant=\"h6\" className={classes.toolbarLabel}>\n {disc?.title || `Untitled Disc`}\n </Typography>\n )}\n {selectedCount === 1 ? (\n <React.Fragment>\n <Tooltip title=\"Move to Position\">\n <Button aria-controls=\"move-menu\" aria-label=\"Move\" onClick={handleShowMoveMenu}>\n Move\n </Button>\n </Tooltip>\n <Menu\n id=\"move-menu\"\n anchorEl={moveMenuAnchorEl}\n open={!!moveMenuAnchorEl}\n onClose={handleCloseMoveMenu}\n PaperProps={{\n style: {\n maxHeight: 300,\n },\n }}\n >\n {Array(tracks.length)\n .fill(null)\n .map((_, i) => {\n return (\n <MenuItem key={`pos-${i}`} onClick={() => handleMoveSelectedTrack(i)}>\n {i + 1}\n </MenuItem>\n );\n })}\n </Menu>\n </React.Fragment>\n ) : null}\n\n {selectedCount > 0 ? (\n <React.Fragment>\n <Tooltip title=\"Record from MD\">\n <Button aria-label=\"Record\" onClick={handleShowDumpDialog}>\n Record\n </Button>\n </Tooltip>\n </React.Fragment>\n ) : null}\n\n {selectedCount > 0 ? (\n <Tooltip title=\"Delete\">\n <IconButton aria-label=\"delete\" onClick={handleDeleteSelected}>\n <DeleteIcon />\n </IconButton>\n </Tooltip>\n ) : null}\n\n {selectedCount > 0 ? (\n <Tooltip title=\"Rename\">\n <IconButton aria-label=\"rename\" disabled={selectedCount !== 1} onClick={handleRenameActionClick}>\n <EditIcon />\n </IconButton>\n </Tooltip>\n ) : null}\n </Toolbar>\n <Box className={classes.main} {...getRootProps()}>\n <input {...getInputProps()} />\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell className={classes.indexCell}>#</TableCell>\n <TableCell>Title</TableCell>\n <TableCell>Format</TableCell>\n <TableCell align=\"right\">Duration</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {tracks.map(track => (\n <TableRow\n hover\n selected={selected.includes(track.index)}\n key={track.index}\n onDoubleClick={event => handleRenameDoubleClick(event, track.index)}\n onClick={event => handleSelectClick(event, track.index)}\n >\n <TableCell className={classes.indexCell}>{track.index + 1}</TableCell>\n <TableCell className={classes.titleCell} title={track.title}>\n {track.title || `No Title`}\n </TableCell>\n <TableCell>\n <span className={classes.formatBadge}>{track.encoding}</span>\n </TableCell>\n <TableCell align=\"right\">{track.duration}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n <Backdrop className={classes.backdrop} open={isDragActive}>\n Drop your Music to Upload\n </Backdrop>\n </Box>\n <Fab color=\"primary\" aria-label=\"add\" className={classes.add} onClick={open}>\n <AddIcon />\n </Fab>\n\n <UploadDialog />\n <RenameDialog />\n <ErrorDialog />\n <ConvertDialog files={uploadedFiles} />\n <RecordDialog />\n <DumpDialog trackIndexes={selected} />\n <AboutDialog />\n </React.Fragment>\n );\n};\n","/* eslint no-restricted-globals: 0 */\nimport { getPublicPathFor } from '../utils';\nexport class AtracdencProcess {\n private messageCallback?: (ev: MessageEvent) => void;\n\n constructor(public worker: Worker) {\n worker.onmessage = this.handleMessage.bind(this);\n }\n\n async init() {\n await new Promise<MessageEvent>(resolve => {\n this.messageCallback = resolve;\n this.worker.postMessage({ action: 'init' });\n });\n }\n\n async encode(data: ArrayBuffer, bitrate: string) {\n let eventData = await new Promise<MessageEvent>(resolve => {\n this.messageCallback = resolve;\n this.worker.postMessage({ action: 'encode', bitrate, data }, [data]);\n });\n return eventData.data.result as Uint8Array;\n }\n\n terminate() {\n this.worker.terminate();\n }\n\n handleMessage(ev: MessageEvent) {\n this.messageCallback!(ev);\n this.messageCallback = undefined;\n }\n}\n\nif (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {\n // Worker\n let Module: any;\n onmessage = async (ev: MessageEvent) => {\n const { action, ...others } = ev.data;\n if (action === 'init') {\n self.importScripts(getPublicPathFor(`atracdenc.js`));\n (self as any).Module().then((m: any) => {\n Module = m;\n self.postMessage({ action: 'init' });\n Module.setLogger && Module.setLogger((msg: string, stream: string) => console.log(`${stream}: ${msg}`));\n });\n } else if (action === 'encode') {\n const { bitrate, data } = others;\n const inWavFile = `inWavFile.wav`;\n const outAt3File = `outAt3File.aea`;\n const dataArray = new Uint8Array(data);\n Module.FS.writeFile(`${inWavFile}`, dataArray);\n Module.callMain([`-e`, `atrac3`, `-i`, inWavFile, `-o`, outAt3File, `--bitrate`, bitrate]);\n\n // Read file and trim header (96 bytes)\n let fileStat = Module.FS.stat(outAt3File);\n let size = fileStat.size;\n let tmp = new Uint8Array(size - 96);\n let outAt3FileStream = Module.FS.open(outAt3File, 'r');\n Module.FS.read(outAt3FileStream, tmp, 0, tmp.length, 96);\n Module.FS.close(outAt3FileStream);\n\n let result = tmp.buffer;\n\n self.postMessage(\n {\n action: 'encode',\n result,\n },\n [result]\n );\n }\n };\n} else {\n // Main\n}\n","import React from 'react';\nimport { useShallowEqualSelector } from '../utils';\nimport { actions as appActions } from '../redux/app-feature';\n\nimport CssBaseline from '@material-ui/core/CssBaseline';\nimport Backdrop from '@material-ui/core/Backdrop';\nimport CircularProgress from '@material-ui/core/CircularProgress';\nimport { makeStyles, createMuiTheme, ThemeProvider } from '@material-ui/core/styles';\n\nimport { Welcome } from './welcome';\nimport { Main } from './main';\nimport { Controls } from './controls';\nimport Paper from '@material-ui/core/Paper';\nimport Typography from '@material-ui/core/Typography';\nimport Link from '@material-ui/core/Link';\nimport Box from '@material-ui/core/Box';\nimport Brightness2Icon from '@material-ui/icons/Brightness2';\nimport IconButton from '@material-ui/core/IconButton';\nimport { useDispatch } from 'react-redux';\n\nconst useStyles = makeStyles(theme => ({\n layout: {\n width: 'auto',\n height: '100%',\n [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {\n width: 600,\n marginLeft: 'auto',\n marginRight: 'auto',\n },\n },\n\n paper: {\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n padding: theme.spacing(2),\n height: '100%',\n [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {\n marginTop: theme.spacing(6),\n marginBottom: theme.spacing(6),\n padding: theme.spacing(3),\n height: 600,\n },\n },\n copyright: {\n display: 'flex',\n alignItems: 'center',\n [theme.breakpoints.down(600 + theme.spacing(2) * 2)]: {\n flexWrap: 'wrap',\n },\n },\n backdrop: {\n zIndex: theme.zIndex.drawer + 1,\n color: '#fff',\n },\n minidiscLogo: {\n width: 48,\n },\n controlsContainer: {\n flex: '1 1 auto',\n paddingLeft: theme.spacing(3),\n paddingRight: theme.spacing(8),\n [theme.breakpoints.down(600 + theme.spacing(2) * 2)]: {\n order: -1,\n width: '100%',\n paddingLeft: 0,\n },\n },\n}));\n\nconst darkTheme = createMuiTheme({\n palette: {\n type: 'dark',\n primary: {\n light: '#6ec6ff',\n main: '#2196f3',\n dark: '#0069c0',\n contrastText: '#fff',\n },\n },\n});\n\nconst lightTheme = createMuiTheme({\n palette: {\n type: 'light',\n },\n});\n\nconst App = () => {\n const classes = useStyles();\n\n const dispatch = useDispatch();\n let { mainView, loading, darkMode } = useShallowEqualSelector(state => state.appState);\n\n return (\n <React.Fragment>\n <ThemeProvider theme={darkMode ? darkTheme : lightTheme}>\n <CssBaseline />\n\n <main className={classes.layout}>\n <Paper className={classes.paper}>\n {mainView === 'WELCOME' ? <Welcome /> : null}\n {mainView === 'MAIN' ? <Main /> : null}\n\n <Box className={classes.copyright}>\n <IconButton onClick={() => dispatch(appActions.setDarkMode(!darkMode))}>\n <Brightness2Icon color={darkMode ? 'secondary' : undefined} />\n </IconButton>\n <Typography variant=\"body2\" color=\"textSecondary\" style={{ marginRight: `8px` }}>\n {'© '}\n <Link rel=\"noopener noreferrer\" color=\"inherit\" target=\"_blank\" href=\"https://stefano.brilli.me/\">\n Stefano Brilli\n </Link>{' '}\n {new Date().getFullYear()}\n {'.'}\n </Typography>\n <Link\n rel=\"noopener noreferrer\"\n href=\"https://twitter.com/share?ref_src=twsrc%5Etfw\"\n className=\"twitter-share-button\"\n data-via=\"thecybercase\"\n data-hashtags=\"MiniDisc\"\n data-dnt=\"true\"\n data-show-count=\"false\"\n >\n Tweet\n </Link>\n <Box className={classes.controlsContainer}>{mainView === 'MAIN' ? <Controls /> : null}</Box>\n </Box>\n </Paper>\n </main>\n\n <Backdrop className={classes.backdrop} open={loading}>\n <CircularProgress color=\"inherit\" />\n </Backdrop>\n </ThemeProvider>\n </React.Fragment>\n );\n};\n\nexport default App;\n","import { createWorker, setLogging } from '@ffmpeg/ffmpeg';\nimport { AtracdencProcess } from './atracdenc-worker';\nimport { getPublicPathFor } from '../utils';\nconst AtracdencWorker = require('worker-loader!./atracdenc-worker'); // eslint-disable-line import/no-webpack-loader-syntax\n\ninterface LogPayload {\n message: string;\n action: string;\n}\n\nexport interface AudioExportService {\n init(): Promise<void>;\n export(params: { format: string }): Promise<ArrayBuffer>;\n info(): Promise<{ format: string | null; input: string | null }>;\n prepare(file: File): Promise<void>;\n}\n\nexport class FFMpegAudioExportService implements AudioExportService {\n public ffmpegProcess: any;\n public atracdencProcess?: AtracdencProcess;\n public loglines: { action: string; message: string }[] = [];\n public inFileName: string = ``;\n public outFileNameNoExt: string = ``;\n\n async init() {\n setLogging(true);\n }\n\n async prepare(file: File) {\n this.loglines = [];\n this.ffmpegProcess = createWorker({\n logger: (payload: LogPayload) => {\n this.loglines.push(payload);\n console.log(payload.action, payload.message);\n },\n corePath: getPublicPathFor('ffmpeg-core.js'),\n workerPath: getPublicPathFor('worker.min.js'),\n });\n await this.ffmpegProcess.load();\n\n this.atracdencProcess = new AtracdencProcess(new AtracdencWorker());\n await this.atracdencProcess.init();\n\n let ext = file.name.split('.').slice(-1);\n if (ext.length === 0) {\n throw new Error(`Unrecognized file format: ${file.name}`);\n }\n\n this.inFileName = `inAudioFile.${ext[0]}`;\n this.outFileNameNoExt = `outAudioFile`;\n\n await this.ffmpegProcess.write(this.inFileName, file);\n }\n\n async info() {\n await this.ffmpegProcess.transcode(this.inFileName, `${this.outFileNameNoExt}.metadata`, `-f ffmetadata`);\n\n let audioFormatRegex = /Audio:\\s(.*?),/; // Actual content\n let inputFormatRegex = /Input #0,\\s(.*?),/; // Container\n let format: string | null = null;\n let input: string | null = null;\n\n for (let line of this.loglines) {\n let match = line.message.match(audioFormatRegex);\n if (match !== null) {\n format = match[1];\n continue;\n }\n match = line.message.match(inputFormatRegex);\n if (match !== null) {\n input = match[1];\n continue;\n }\n if (format !== null && input !== null) {\n break;\n }\n }\n\n return { format, input };\n }\n\n async export({ format }: { format: string }) {\n let result: ArrayBuffer;\n if (format === `SP`) {\n const outFileName = `${this.outFileNameNoExt}.raw`;\n await this.ffmpegProcess.transcode(this.inFileName, outFileName, '-f s16be -ar 44100');\n let { data } = await this.ffmpegProcess.read(outFileName);\n result = data.buffer;\n } else {\n const outFileName = `${this.outFileNameNoExt}.wav`;\n await this.ffmpegProcess.transcode(this.inFileName, outFileName, '-f wav -ar 44100');\n let { data } = await this.ffmpegProcess.read(outFileName);\n let bitrate: string = `0`;\n switch (format) {\n case `LP2`:\n bitrate = `128`;\n break;\n case `LP105`:\n bitrate = `102`;\n break;\n case `LP4`:\n bitrate = `64`;\n break;\n }\n result = await this.atracdencProcess!.encode(data.buffer, bitrate);\n }\n this.ffmpegProcess.worker.terminate();\n this.atracdencProcess!.terminate();\n return result;\n }\n}\n","/* eslint no-restricted-globals: 0 */\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport * as serviceWorker from './serviceWorker';\nimport { NetMDUSBService } from './services/netmd';\nimport { NetMDMockService } from './services/netmd-mock';\nimport serviceRegistry from './services/registry';\n\nimport { store } from './redux/store';\nimport { actions as appActions } from './redux/app-feature';\n\nimport App from './components/app';\n\nimport './index.css';\nimport { FFMpegAudioExportService } from './services/audio-export';\nimport { MediaRecorderService } from './services/mediarecorder';\n\nserviceRegistry.netmdService = new NetMDUSBService({ debug: true });\n// serviceRegistry.netmdService = new NetMDMockService(); // Uncomment to work without a device attached\nserviceRegistry.audioExportService = new FFMpegAudioExportService();\nserviceRegistry.mediaRecorderService = new MediaRecorderService();\n\n(function setupEventHandlers() {\n window.addEventListener('beforeunload', ev => {\n let isUploading = store.getState().uploadDialog.visible;\n if (!isUploading) {\n return;\n }\n ev.preventDefault();\n ev.returnValue = `Warning! Recording will be interrupted`;\n });\n\n if (navigator && navigator.usb) {\n navigator.usb.ondisconnect = function() {\n store.dispatch(appActions.setState('WELCOME'));\n };\n } else {\n store.dispatch(appActions.setBrowserSupported(false));\n }\n\n // eslint-disable-next-line\n let deferredPrompt: any;\n window.addEventListener('beforeinstallprompt', (e: any) => {\n e.preventDefault();\n deferredPrompt = e;\n });\n})();\n\nReactDOM.render(\n <Provider store={store}>\n <App />\n </Provider>,\n document.getElementById('root')\n);\n\n// serviceWorker.unregister();\nserviceWorker.register();\n","import { sanitizeTitle, getPublicPathFor } from '../utils';\nimport Recorder from 'recorderjs';\n\nexport class MediaRecorderService {\n public recorder: any;\n public stream?: MediaStream;\n public audioContext?: AudioContext;\n public analyserNode?: AnalyserNode;\n public gainNode?: GainNode;\n\n playTestInput(deviceId: string) {\n this.audioContext = new AudioContext();\n this.gainNode = this.audioContext.createGain();\n this.analyserNode = this.audioContext.createAnalyser();\n\n this.initStream(deviceId).then(() => {\n const source = this.audioContext!.createMediaStreamSource(this.stream!);\n source.connect(this.gainNode!);\n this.gainNode!.connect(this.analyserNode!);\n this.analyserNode!.connect(this.audioContext!.destination);\n });\n }\n\n stopTestInput() {\n if (!this.audioContext) {\n return;\n }\n this.audioContext?.close();\n delete this.audioContext;\n this.closeStream();\n }\n\n async initStream(deviceId: string) {\n const recordConstraints = {\n // Try to set the best recording params for ripping the audio tracks\n autoGainControl: false,\n channelCount: 2,\n deviceId: deviceId,\n echoCancellation: false,\n noiseSuppression: false,\n sampleRate: 44100,\n highpassFilter: false,\n };\n this.stream = await navigator.mediaDevices.getUserMedia({ audio: recordConstraints });\n\n // Dump recording settings\n const audioTracks = this.stream.getAudioTracks();\n if (audioTracks.length > 0) {\n console.log('Record Setings:', audioTracks[0].getSettings());\n }\n }\n\n async startRecording() {\n this.audioContext = new AudioContext();\n const input = this.audioContext.createMediaStreamSource(this.stream!);\n this.recorder = new Recorder(input, { workerPath: getPublicPathFor(`recorderWorker.js`) });\n this.recorder.record();\n }\n\n async stopRecording() {\n this.recorder.stop();\n }\n\n async closeStream() {\n this.stream?.getTracks().forEach(track => track.stop());\n }\n\n downloadRecorded(title: string) {\n this.recorder.exportWAV((buffer: Blob) => {\n let url = URL.createObjectURL(buffer);\n let a = document.createElement('a');\n document.body.appendChild(a);\n a.style.display = 'none';\n a.href = url;\n a.download = `${sanitizeTitle(title)}.wav`;\n a.click();\n window.URL.revokeObjectURL(url);\n document.body.removeChild(a);\n });\n }\n}\n"],"sourceRoot":""}