From 40c8b3db1565fd25d6c26843f86c95087067f79f Mon Sep 17 00:00:00 2001 From: florian <> Date: Tue, 16 Jul 2024 23:11:24 +0200 Subject: [PATCH] feat: Improved audio publishing (genres, thumbnail) --- TODO.md | 4 + src/components/CheckBox/CheckBox.tsx | 10 +- .../FileEventEditor/FileEventEditor.tsx | 95 ++++- .../FileEventEditor/usePublishing.ts | 34 +- src/pages/Upload.tsx | 303 +++++++++------- src/utils/genres.ts | 328 ++++++++++++++++++ src/utils/transfer.ts | 27 +- 7 files changed, 643 insertions(+), 158 deletions(-) create mode 100644 TODO.md create mode 100644 src/utils/genres.ts diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..c25ad21 --- /dev/null +++ b/TODO.md @@ -0,0 +1,4 @@ + + +- When 403 from nogood, error should be displayed. +- Delete does not work in the ALL SERVERS view diff --git a/src/components/CheckBox/CheckBox.tsx b/src/components/CheckBox/CheckBox.tsx index a0d599f..cb6cc45 100644 --- a/src/components/CheckBox/CheckBox.tsx +++ b/src/components/CheckBox/CheckBox.tsx @@ -1,14 +1,16 @@ +import { ReactNode } from "react"; + const CheckBox = ({ name, checked, setChecked, - label, + children, disabled = false, }: { name: string; checked: boolean; setChecked: (checked: boolean) => void; - label: string; + children: ReactNode; disabled: boolean; }) => ( <> @@ -20,8 +22,8 @@ const CheckBox = ({ checked={checked} onChange={e => setChecked(e.currentTarget.checked)} /> - + +
+
+

Servers

+
+ {servers.map(s => ( + + setTransfers(ut => ({ ...ut, [s.name]: { enabled: c, transferred: 0, size: 0, rate: 0 } })) + } + > + + {s.name}
{serverInfo[s.name].type}
+
+ ))} +
+
+ {imagesAreUploaded && ( +
+

Image Options

+
+ setCleanPrivateData(c)} + > + Clean private data in images (EXIF) + + 0} + onChange={() => setImageResize(irs => (irs > 0 ? 0 : 1))} + /> +
+ + +
+
+
+ )} +
+ +
+ + +
+ + )} + + {uploadStep == 1 && ( + <> +

Servers

+
+ {servers.map( + s => + transfers[s.name]?.enabled && ( + <> + {s.name} +
+ 0 ? '' + formatFileSize(transfers[s.name].rate) + '/s' : '' + } + /> + {transfers[s.name].error && ( +
{transfers[s.name].error}
+ )} +
+ + ) + )} +
+ + )} )} {fileEventsToPublish.length > 0 && ( diff --git a/src/utils/genres.ts b/src/utils/genres.ts new file mode 100644 index 0000000..6085c04 --- /dev/null +++ b/src/utils/genres.ts @@ -0,0 +1,328 @@ +import { groupBy, mapValues } from "lodash"; + +// https://raw.githubusercontent.com/wavlake/genre-list/main/list.csv +const wavlakeGenres = [ +['Alternative','Alternative Rock'], +['Alternative','College Rock'], +['Alternative','Experimental Rock'], +['Alternative','Goth Rock'], +['Alternative','Grunge'], +['Alternative','Hardcore Punk'], +['Alternative','Hard Rock'], +['Alternative','Indie Rock'], +['Alternative','New Wave'], +['Alternative','Progressive Rock'], +['Alternative','Punk'], +['Alternative','Shoegaze'], +['Alternative','Steampunk'], +['Blues','Acoustic Blues'], +['Blues','Chicago Blues'], +['Blues','Classic Blues'], +['Blues','Contemporary Blues'], +['Blues','Country Blues'], +['Blues','Delta Blues'], +['Blues','Electric Blues'], +['Children’s Music','Lullabies'], +['Children’s Music','Sing-Along'], +['Children’s Music','Stories'], +['Classical','Avant-Garde'], +['Classical','Baroque'], +['Classical','Chamber Music'], +['Classical','Chant'], +['Classical','Choral'], +['Classical','Classical Crossover'], +['Classical','Early Music'], +['Classical','High Classical'], +['Classical','Impressionist'], +['Classical','Medieval'], +['Classical','Minimalism'], +['Classical','Modern Composition'], +['Classical','Opera'], +['Classical','Orchestral'], +['Classical','Renaissance'], +['Classical','Romantic'], +['Classical','Wedding Music'], +['Comedy','Novelty'], +['Comedy','Standup Comedy'], +['Country','Alternative Country'], +['Country','Americana'], +['Country','Bluegrass'], +['Country','Contemporary Bluegrass'], +['Country','Contemporary Country'], +['Country','Country Gospel'], +['Country','Honky Tonk'], +['Country','Outlaw Country'], +['Country','Traditional Bluegrass'], +['Country','Traditional Country'], +['Country','Urban Cowboy'], +['Dance/EDM','Breakbeat'], +['Dance/EDM','Dubstep'], +['Dance/EDM','Exercise'], +['Dance/EDM','Garage'], +['Dance/EDM','Hardcore'], +['Dance/EDM','Hard Dance'], +['Dance/EDM','Hi-NRG / Eurodance'], +['Dance/EDM','House'], +['Dance/EDM','Jackin House'], +['Dance/EDM','Jungle/Drum’n\'bass'], +['Dance/EDM','Techno'], +['Dance/EDM','Trance'], +['Easy Listening','Bop'], +['Easy Listening','Lounge'], +['Easy Listening','Swing'], +['Electronic','Ambient'], +['Electronic','Crunk'], +['Electronic','Downtempo'], +['Electronic','Electro'], +['Electronic','Electronica'], +['Electronic','Electronic Rock'], +['Electronic','IDM/Experimental'], +['Electronic','Industrial'], +['Hip-Hop/Rap','Alternative Rap'], +['Hip-Hop/Rap','Bounce'], +['Hip-Hop/Rap','Dirty South'], +['Hip-Hop/Rap','East Coast Rap'], +['Hip-Hop/Rap','Gangsta Rap'], +['Hip-Hop/Rap','Hardcore Rap'], +['Hip-Hop/Rap','Hip-Hop'], +['Hip-Hop/Rap','Latin Rap'], +['Hip-Hop/Rap','Old School Rap'], +['Hip-Hop/Rap','Rap'], +['Hip-Hop/Rap','Underground Rap'], +['Hip-Hop/Rap','West Coast Rap'], +['Holiday','Chanukah'], +['Holiday','Christmas'], +['Holiday','Christmas: Children’s'], +['Holiday','Christmas: Classic'], +['Holiday','Christmas: Classical'], +['Holiday','Christmas: Jazz'], +['Holiday','Christmas: Modern'], +['Holiday','Christmas: Pop'], +['Holiday','Christmas: R&B'], +['Holiday','Christmas: Religious'], +['Holiday','Christmas: Rock'], +['Holiday','Easter'], +['Holiday','Halloween'], +['Holiday','Holiday: Other'], +['Holiday','Thanksgiving'], +['Inspirational – Christian & Gospel','CCM'], +['Inspirational – Christian & Gospel','Christian Metal'], +['Inspirational – Christian & Gospel','Christian Pop'], +['Inspirational – Christian & Gospel','Christian Rap'], +['Inspirational – Christian & Gospel','Christian Rock'], +['Inspirational – Christian & Gospel','Classic Christian'], +['Inspirational – Christian & Gospel','Contemporary Gospel'], +['Inspirational – Christian & Gospel','Gospel'], +['Inspirational – Christian & Gospel','Christian & Gospel'], +['Inspirational – Christian & Gospel','Praise & Worship'], +['Inspirational – Christian & Gospel','Qawwali'], +['Inspirational – Christian & Gospel','Southern Gospel'], +['Inspirational – Christian & Gospel','Traditional Gospel'], +['Instrumental','March (Marching Band)'], +['Instrumental','Karaoke'], +['Jazz','Acid Jazz'], +['Jazz','Avant-Garde Jazz'], +['Jazz','Big Band'], +['Jazz','Blue Note'], +['Jazz','Contemporary Jazz'], +['Jazz','Cool'], +['Jazz','Crossover Jazz'], +['Jazz','Dixieland'], +['Jazz','Ethio-jazz'], +['Jazz','Fusion'], +['Jazz','Hard Bop'], +['Jazz','Latin Jazz'], +['Jazz','Mainstream Jazz'], +['Jazz','Ragtime'], +['Jazz','Smooth Jazz'], +['Jazz','Trad Jazz'], +['Latino','Alternativo & Rock Latino'], +['Latino','Baladas y Boleros'], +['Latino','Brazilian'], +['Latino','Contemporary Latin'], +['Latino','Latin Jazz'], +['Latino','Pop Latino'], +['Latino','Raíces'], +['Latino','Reggaeton y Hip-Hop'], +['Latino','Regional Mexicano'], +['Latino','Salsa y Tropical'], +['New Age','Environmental'], +['New Age','Healing'], +['New Age','Meditation'], +['New Age','Nature'], +['New Age','Relaxation'], +['New Age','Travel'], +['Pop','Adult Contemporary'], +['Pop','Britpop'], +['Pop','Pop/Rock'], +['Pop','Soft Rock'], +['Pop','Teen Pop'], +['Pop','Indie Pop'], +['Pop','Anime'], +['Pop','K-Pop'], +['Pop','J-Pop'], +['Pop','French Pop'], +['Pop','German Pop'], +['R&B/Soul','Contemporary R&B'], +['R&B/Soul','Disco'], +['R&B/Soul','Doo Wop'], +['R&B/Soul','Funk'], +['R&B/Soul','Motown'], +['R&B/Soul','Neo-Soul'], +['R&B/Soul','Quiet Storm'], +['R&B/Soul','Soul'], +['Reggae','Dancehall'], +['Reggae','Dub'], +['Reggae','Roots Reggae'], +['Reggae','Ska'], +['Rock','Adult Alternative'], +['Rock','American Trad Rock'], +['Rock','Arena Rock'], +['Rock','Blues-Rock'], +['Rock','British Invasion'], +['Rock','Death Metal/Black Metal'], +['Rock','Glam Rock'], +['Rock','Hair Metal'], +['Rock','Hard Rock'], +['Rock','Metal'], +['Rock','Jam Bands'], +['Rock','Prog-Rock/Art Rock'], +['Rock','Psychedelic'], +['Rock','Rock & Roll'], +['Rock','Rockabilly'], +['Rock','Roots Rock'], +['Rock','Singer/Songwriter'], +['Rock','Southern Rock'], +['Rock','Surf'], +['Rock','Tex-Mex'], +['Singer/Songwriter','Alternative Folk'], +['Singer/Songwriter','Contemporary Folk'], +['Singer/Songwriter','Contemporary Singer/Songwriter'], +['Singer/Songwriter','Folk-Rock'], +['Singer/Songwriter','New Acoustic'], +['Singer/Songwriter','Traditional Folk'], +['Singer/Songwriter','German Folk'], +['Soundtrack','Foreign Cinema'], +['Soundtrack','Musicals'], +['Soundtrack','Original Score'], +['Soundtrack','Soundtrack'], +['Soundtrack','TV Soundtrack'], +['Spoken Word'], +['Tex-Mex/Tejano','Chicano'], +['Tex-Mex/Tejano','Classic'], +['Tex-Mex/Tejano','Conjunto'], +['Tex-Mex/Tejano','Conjunto Progressive'], +['Tex-Mex/Tejano','New Mex'], +['Tex-Mex/Tejano','Tex-Mex'], +['Vocal','Barbershop'], +['Vocal','Doo-wop'], +['Vocal','Standards'], +['Vocal','Traditional Pop'], +['Vocal','Vocal Jazz'], +['Vocal','Vocal Pop'], +['Vocal','Enka'], +['Vocal','Sea Shanty'], +['Africa','African Heavy Metal'], +['Africa','African Hip Hop'], +['Africa','Afro-Beat'], +['Africa','Afro-House'], +['Africa','Afro-Pop'], +['Africa','Apala/Akpala'], +['Africa','Benga'], +['Africa','Bikutsi'], +['Africa','Bongo Flava'], +['Africa','Cape Jazz'], +['Africa','Chimurenga'], +['Africa','Coupé-Décalé'], +['Africa','Fuji Music'], +['Africa','Genge'], +['Africa','Gnawa'], +['Africa','Highlife'], +['Africa','Hiplife'], +['Africa','Isicathamiya'], +['Africa','Jit'], +['Africa','Jùjú'], +['Africa','Kapuka'], +['Africa','Kizomba'], +['Africa','Kuduro'], +['Africa','Kwaito'], +['Africa','Kwela'], +['Africa','Lingala'], +['Africa','Makossa'], +['Africa','Maloya'], +['Africa','Marrabenta'], +['Africa','Mbalax'], +['Africa','Mbaqanga'], +['Africa','Mbube'], +['Africa','Morna'], +['Africa','Museve'], +['Africa','Negro Spiritual'], +['Africa','Palm-Wine'], +['Africa','Raï'], +['Africa','Sakara'], +['Africa','Sega'], +['Africa','Seggae'], +['Africa','Semba'], +['Africa','Soukous'], +['Africa','Taarab'], +['Africa','Zouglou'], +['Asia','Anison'], +['Asia','Baithak Gana'], +['Asia','C-Pop'], +['Asia','CityPop'], +['Asia','Cantopop'], +['Asia','Enka'], +['Asia','Hong Kong English Pop'], +['Asia','Fann At-Tanbura'], +['Asia','Fijiri'], +['Asia','Khaliji'], +['Asia','Kayōkyoku'], +['Asia','Liwa'], +['Asia','Mandopop'], +['Asia','Onkyokei'], +['Asia','Taiwanese Pop'], +['Asia','Thai Pop'], +['Asia','Sawt'], +['World','Cajun'], +['World','Calypso'], +['Caribbean','Chutney'], +['Caribbean','Chutney Soca'], +['Caribbean','Compas'], +['Caribbean','Mambo'], +['Caribbean','Merengue'], +['Caribbean','Méringue'], +['World','Carnatic (Karnataka Sanghetha)'], +['World','Celtic'], +['World','Celtic Folk'], +['World','Contemporary Celtic'], +['World','Coupé-décalé – Congo'], +['World','Dangdut'], +['World','Drinking Songs'], +['World','Drone'], +['World','Klezmer'], +['World','Mbalax – Senegal'], +['World','Polka'], +['World','Soca'], +['World','Baila'], +['World','Bhangra'], +['World','Bhojpuri'], +['World','Dangdut'], +['World','Filmi'], +['World','Indian Pop'], +['World','Hindustani'], +['World','Indian Ghazal'], +['World','Lavani'], +['World','Luk Thung'], +['World','Luk Krung'], +['World','Manila Sound'], +['World','Morlam'], +['World','Pinoy Pop'], +['World','Pop Sunda'], +['World','Ragini'], +['World','Thai Pop'], +['World','Traditional Celtic'], +['World','Worldbeat'], +['World','Zydeco'] +]; + +export const allGenres = mapValues(groupBy(wavlakeGenres, g => g[0]), v => v.flatMap(x => x[1]).filter(x => !!x) ); \ No newline at end of file diff --git a/src/utils/transfer.ts b/src/utils/transfer.ts index dbebc8b..2faaac4 100644 --- a/src/utils/transfer.ts +++ b/src/utils/transfer.ts @@ -61,6 +61,13 @@ export const mirrordBlob = async ( return res.data; }; +async function blobUrlToFile(blobUrl: string, fileName: string): Promise { + const response = await fetch(blobUrl); + const blob = await response.blob(); + const fileOptions = { type: blob.type, lastModified: Date.now() }; + return new File([blob], fileName, fileOptions); +} + export const transferBlob = async ( sourceUrl: string, targetServer: string, @@ -69,15 +76,21 @@ export const transferBlob = async ( ): Promise => { console.log({ sourceUrl, targetServer }); - const blob = await mirrordBlob(targetServer, sourceUrl, signEventTemplate); - if (blob) return blob; - console.log('Mirror failed. Using download + upload instead.'); + if (sourceUrl.startsWith('blob:')) { + const file = await blobUrlToFile(sourceUrl, 'cover.jpg'); + return await uploadBlob(targetServer, file, signEventTemplate, onUploadProgress); - const result = await downloadBlob(sourceUrl, onUploadProgress); + } else { + const blob = await mirrordBlob(targetServer, sourceUrl, signEventTemplate); + if (blob) return blob; + console.log('Mirror failed. Using download + upload instead.'); - const fileName = sourceUrl.replace(/.*\//, ''); + const result = await downloadBlob(sourceUrl, onUploadProgress); - const file = new File([result.data], fileName, { type: result.type, lastModified: new Date().getTime() }); + const fileName = sourceUrl.replace(/.*\//, ''); - return await uploadBlob(targetServer, file, signEventTemplate, onUploadProgress); + const file = new File([result.data], fileName, { type: result.type, lastModified: new Date().getTime() }); + + return await uploadBlob(targetServer, file, signEventTemplate, onUploadProgress); + } };