merge dev NOT READY TO MERGE INTO MAIN

This commit is contained in:
Amaury Danis Cousandier
2024-01-12 19:28:20 +01:00
151 changed files with 1187 additions and 1274 deletions

View File

@@ -16,9 +16,10 @@ GOOGLE_CALLBACK_URL=http://localhost:19006/logged/google
SMTP_TRANSPORT=smtps://toto:tata@relay
MAIL_AUTHOR='"Chromacase" <chromacase@octohub.app>'
IGNORE_MAILS=true
API_KEYS=SCOROTEST,ROBOTO,SCORO
API_KEYS=SCOROTEST,ROBOTO,SCORO,POPULATE
API_KEY_SCORO_TEST=SCOROTEST
API_KEY_ROBOT=ROBOTO
API_KEY_SCORO=SCORO
API_KEY_POPULATE=POPULATE
MEILI_MASTER_KEY="ghvjkgisbgkbgskegblfqbgjkebbhgwkjfb"
# vi: ft=sh

19
assets/create_melodies.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Iterate through subfolders
find . -type d | while read -r dir; do
# Check if .midi file exists in the subfolder
midi_file=$(find "$dir" -maxdepth 1 -type f -name '*.midi' | head -n 1)
if [ -n "$midi_file" ]; then
# Create output file name (melody.mp3) in the same subfolder
output_file="${dir}/melody.mp3"
# Run the given command
#timidity "$midi_file" -Ow -o - | ffmpeg -i - -acodec libmp3lame -ab 64k "$output_file"
fluidsynth -a alsa -T raw -F - "$midi_file" | ffmpeg -f s32le -i - "$output_file"
echo "Converted: $midi_file to $output_file"
fi
done

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -177,6 +177,31 @@ export class SongController {
}
}
@Get(":id/assets/melody")
@ApiOperation({
description: "Streams the mp3 file of the requested song",
})
@ApiNotFoundResponse({ description: "Song not found" })
@ApiOkResponse({ description: "Returns the mp3 file succesfully" })
@Header("Cache-Control", "max-age=86400")
@Header("Content-Type", "audio/mpeg")
@Public()
async getMelody(@Param("id", ParseIntPipe) id: number) {
const song = await this.songService.song({ id });
if (!song) throw new NotFoundException("Song not found");
const path = song.musicXmlPath;
// mp3 file is next to the musicXML file and called melody.mp3
const pathWithoutFile = path.substring(0, path.lastIndexOf("/"));
try {
const file = createReadStream(pathWithoutFile + "/melody.mp3");
return new StreamableFile(file, { type: "audio/mpeg" });
} catch {
throw new InternalServerErrorException();
}
}
@Post()
@ApiOperation({
description:

View File

@@ -67,6 +67,7 @@ services:
- NGINX_PORT=4567
ports:
- "19006:19006"
- "8081:8081"
volumes:
- ./front:/app
depends_on:

View File

@@ -1,4 +0,0 @@
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}

View File

@@ -721,5 +721,9 @@ export default class API {
);
},
};
}
public static getPartitionMelodyUrl(songId: number): string {
return `${API.baseUrl}/song/${songId}/assets/melody`;
}
}

View File

@@ -4,4 +4,4 @@ WORKDIR /app
RUN yarn global add npx expo-cli
ENV DEVAPIURL http://back:3000
CMD npx expo start --web
CMD npx expo start --web

View File

@@ -1,11 +1,11 @@
/* eslint-disable @typescript-eslint/ban-types */
import { NativeStackScreenProps, createNativeStackNavigator } from '@react-navigation/native-stack';
import {
NavigationProp,
ParamListBase,
useNavigation as navigationHook,
} from '@react-navigation/native';
import React, { useEffect, useMemo } from 'react';
NativeStackNavigationProp,
NativeStackScreenProps,
createNativeStackNavigator,
} from '@react-navigation/native-stack';
import { ParamListBase, useNavigation as navigationHook } from '@react-navigation/native';
import React, { ComponentProps, ComponentType, useEffect, useMemo } from 'react';
import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
import { RootState, useSelector } from './state/Store';
import { useDispatch } from 'react-redux';
@@ -33,153 +33,201 @@ import ForgotPasswordView from './views/ForgotPasswordView';
import DiscoveryView from './views/V2/DiscoveryView';
import MusicView from './views/MusicView';
import Leaderboardiew from './views/LeaderboardView';
import { LinearGradient } from 'expo-linear-gradient';
import { createCustomNavigator } from './utils/navigator';
import { Cup, Discover, Music, SearchNormal1, Setting2, User } from 'iconsax-react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const Stack = createNativeStackNavigator<AppRouteParams>();
const Tab = createCustomNavigator<AppRouteParams>();
const Tabs = () => {
return (
<Tab.Navigator>
{Object.entries(tabRoutes).map(([name, route], routeIndex) => (
<Tab.Screen
key={'route-' + routeIndex}
name={name as keyof AppRouteParams}
options={{ ...route.options, headerTransparent: true }}
component={route.component}
/>
))}
</Tab.Navigator>
);
};
// Util function to hide route props in URL
const removeMe = () => '';
const protectedRoutes = () =>
({
Home: {
component: DiscoveryView,
options: { headerShown: false },
link: '/',
const tabRoutes = {
Home: {
component: DiscoveryView,
options: { headerShown: false, tabBarIcon: Discover },
link: '/',
},
User: {
component: ProfileView,
options: { headerShown: false, tabBarIcon: User },
link: '/user',
},
Music: {
component: MusicView,
options: { headerShown: false, tabBarIcon: Music },
link: '/music',
},
Search: {
component: SearchView,
options: { headerShown: false, tabBarIcon: SearchNormal1 },
link: '/search/:query?',
},
Leaderboard: {
component: Leaderboardiew,
options: { title: translate('leaderboardTitle'), headerShown: false, tabBarIcon: Cup },
link: '/leaderboard',
},
Settings: {
component: SettingsTab,
options: { headerShown: false, tabBarIcon: Setting2, subMenu: true },
link: '/settings/:screen?',
stringify: {
screen: removeMe,
},
Music: {
component: MusicView,
options: { headerShown: false },
link: '/music',
},
Play: {
component: PlayView,
options: { headerShown: false, title: translate('play') },
link: '/play/:songId',
},
Settings: {
component: SettingsTab,
options: { headerShown: false },
link: '/settings/:screen?',
stringify: {
screen: removeMe,
},
},
Artist: {
component: ArtistDetailsView,
options: { title: translate('artistFilter') },
link: '/artist/:artistId',
},
Genre: {
component: GenreDetailsView,
options: { title: translate('genreFilter') },
link: '/genre/:genreId',
},
Search: {
component: SearchView,
options: { headerShown: false },
link: '/search/:query?',
},
Leaderboard: {
component: Leaderboardiew,
options: { title: translate('leaderboardTitle'), headerShown: false },
link: '/leaderboard',
},
Error: {
component: ErrorView,
options: { title: translate('error'), headerLeft: null },
link: undefined,
},
User: { component: ProfileView, options: { headerShown: false }, link: '/user' },
Verified: {
component: VerifiedView,
options: { title: 'Verify email', headerShown: false },
link: '/verify',
},
}) as const;
},
};
const publicRoutes = () =>
({
Login: {
component: SigninView,
options: { title: translate('signInBtn'), headerShown: false },
link: '/login',
},
Signup: {
component: SignupView,
options: { title: translate('signUpBtn'), headerShown: false },
link: '/signup',
},
Google: {
component: GoogleView,
options: { title: 'Google signin', headerShown: false },
link: '/logged/google',
},
PasswordReset: {
component: PasswordResetView,
options: { title: 'Password reset form', headerShown: false },
link: '/password_reset',
},
ForgotPassword: {
component: ForgotPasswordView,
options: { title: 'Password reset form', headerShown: false },
link: '/forgot_password',
},
}) as const;
const protectedRoutes = {
Tabs: {
component: Tabs,
options: { headerShown: false, path: '' },
link: '',
childRoutes: tabRoutes,
},
Play: {
component: PlayView,
options: { headerShown: false, title: translate('play') },
link: '/play/:songId',
},
Artist: {
component: ArtistDetailsView,
options: { title: translate('artistFilter') },
link: '/artist/:artistId',
},
Genre: {
component: GenreDetailsView,
options: { title: translate('genreFilter') },
link: '/genre/:genreId',
},
Error: {
component: ErrorView,
options: { title: translate('error'), headerLeft: null },
link: undefined,
},
Verified: {
component: VerifiedView,
options: { title: 'Verify email', headerShown: false },
link: '/verify',
},
};
const publicRoutes = {
Login: {
component: SigninView,
options: { title: translate('signInBtn'), headerShown: false },
link: '/login',
},
Signup: {
component: SignupView,
options: { title: translate('signUpBtn'), headerShown: false },
link: '/signup',
},
Google: {
component: GoogleView,
options: { title: 'Google signin', headerShown: false },
link: '/logged/google',
},
PasswordReset: {
component: PasswordResetView,
options: { title: 'Password reset form', headerShown: false },
link: '/password_reset',
},
ForgotPassword: {
component: ForgotPasswordView,
options: { title: 'Password reset form', headerShown: false },
link: '/forgot_password',
},
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Route<Props = any> = {
component: (arg: RouteProps<Props>) => JSX.Element | (() => JSX.Element);
component: ComponentType<Props>;
options: object;
link?: string;
};
type OmitOrUndefined<T, K extends string> = T extends undefined ? T : Omit<T, K>;
// if the component has no props, ComponentProps return unknown so we remove those
type RemoveNonObjects<T> = [T] extends [{}] ? T : undefined;
type RouteParams<Routes extends Record<string, Route>> = {
[RouteName in keyof Routes]: OmitOrUndefined<
Parameters<Routes[RouteName]['component']>[0],
keyof NativeStackScreenProps<{}>
>;
[RouteName in keyof Routes]: RemoveNonObjects<ComponentProps<Routes[RouteName]['component']>>;
};
type PrivateRoutesParams = RouteParams<ReturnType<typeof protectedRoutes>>;
type PublicRoutesParams = RouteParams<ReturnType<typeof publicRoutes>>;
type AppRouteParams = PrivateRoutesParams & PublicRoutesParams;
type PrivateRoutesParams = RouteParams<typeof protectedRoutes>;
type PublicRoutesParams = RouteParams<typeof publicRoutes>;
type TabsRoutesParams = RouteParams<typeof tabRoutes>;
type AppRouteParams = Omit<PrivateRoutesParams, 'Tabs'> & {
Tabs: { screen: keyof TabsRoutesParams };
} & PublicRoutesParams &
TabsRoutesParams & { Oops: undefined };
const Stack = createNativeStackNavigator<AppRouteParams & { Loading: never; Oops: never }>();
const RouteToScreen = <T extends {}>(Component: Route<T>['component']) =>
function Route(props: NativeStackScreenProps<T & ParamListBase>) {
const colorScheme = useColorScheme();
const insets = useSafeAreaInsets();
const RouteToScreen =
<T extends {}>(component: Route<T>['component']) =>
// eslint-disable-next-line react/display-name
(props: NativeStackScreenProps<T & ParamListBase>) => (
<>
{component({ ...props.route.params, route: props.route } as Parameters<
Route<T>['component']
>[0])}
</>
);
return (
<LinearGradient
colors={colorScheme === 'dark' ? ['#101014', '#6075F9'] : ['#cdd4fd', '#cdd4fd']}
style={{
flex: 1,
paddingTop: insets.top,
paddingBottom: insets.bottom,
paddingLeft: insets.left,
paddingRight: insets.right,
}}
>
<Component {...(props.route.params as T)} route={props.route} />
</LinearGradient>
);
};
const routesToScreens = (routes: Partial<Record<keyof AppRouteParams, Route>>) =>
Object.entries(routes).map(([name, route], routeIndex) => (
<Stack.Screen
key={'route-' + routeIndex}
name={name as keyof AppRouteParams}
options={route.options}
options={{ ...route.options, headerTransparent: true }}
component={RouteToScreen(route.component)}
/>
));
const routesToLinkingConfig = (
routes: Partial<
Record<keyof AppRouteParams, { link?: string; stringify?: Record<string, () => string> }>
>
) => {
type RouteDescription = Record<
string,
{ link?: string; stringify?: Record<string, () => string>; childRoutes?: RouteDescription }
>;
const routesToLinkingConfig = (routes: RouteDescription) => {
// Too lazy to (find the) type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pagesToRoute = {} as Record<keyof AppRouteParams, any>;
Object.keys(routes).forEach((route) => {
const index = route as keyof AppRouteParams;
if (routes[index]?.link) {
if (routes[index]?.link !== undefined) {
pagesToRoute[index] = {
path: routes[index]!.link!,
stringify: routes[index]!.stringify,
screens: routes[index]!.childRoutes
? routesToLinkingConfig(routes[index]!.childRoutes!).config.screens
: undefined,
};
}
});
@@ -239,12 +287,6 @@ export const Router = () => {
}
return 'noAuth';
}, [userProfile, accessToken]);
const routes = useMemo(() => {
if (authStatus == 'authed') {
return protectedRoutes();
}
return publicRoutes();
}, [authStatus]);
useEffect(() => {
if (accessToken) {
@@ -257,13 +299,14 @@ export const Router = () => {
return <LoadingView />;
}
const routes = authStatus == 'authed' ? { ...protectedRoutes } : publicRoutes;
return (
<NavigationContainer
linking={routesToLinkingConfig(routes)}
fallback={<LoadingView />}
theme={colorScheme == 'light' ? DefaultTheme : DarkTheme}
>
<Stack.Navigator>
<Stack.Navigator screenOptions={{ navigationBarColor: 'transparent' }}>
{authStatus == 'error' ? (
<>
<Stack.Screen
@@ -272,7 +315,7 @@ export const Router = () => {
<ProfileErrorView onTryAgain={() => userProfile.refetch()} />
))}
/>
{routesToScreens(publicRoutes())}
{routesToScreens(publicRoutes)}
</>
) : (
routesToScreens(routes)
@@ -282,6 +325,4 @@ export const Router = () => {
);
};
export type RouteProps<T> = T & Pick<NativeStackScreenProps<T & ParamListBase>, 'route'>;
export const useNavigation = () => navigationHook<NavigationProp<AppRouteParams>>();
export const useNavigation = () => navigationHook<NativeStackNavigationProp<AppRouteParams>>();

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More