This commit is contained in:
2026-07-24 13:14:49 +02:00
parent 8c42077112
commit f5154a87e0
78 changed files with 4474 additions and 453 deletions
+10 -1
View File
@@ -3,7 +3,7 @@
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<annotationProcessing> <annotationProcessing>
<profile default="true" name="Default" enabled="true" /> <profile default="true" name="Default" enabled="true" />
<profile name="Annotation profile for mpv-client" enabled="true"> <profile name="Annotation profile for musix" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" /> <sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" /> <outputRelativeToContentRoot value="true" />
@@ -11,6 +11,15 @@
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar" /> <entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar" />
</processorPath> </processorPath>
<module name="musix" /> <module name="musix" />
</profile>
<profile name="Annotation profile for mpv-client" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar" />
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar" />
</processorPath>
<module name="mpv-client" /> <module name="mpv-client" />
</profile> </profile>
</annotationProcessing> </annotationProcessing>
Generated
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/frontend" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
</component> </component>
</project> </project>
+7
View File
@@ -0,0 +1,7 @@
{
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.24.0"
}
}
+1451 -1
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -3,7 +3,7 @@
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve --host 0.0.0.0 --port 4200",
"build": "ng build", "build": "ng build",
"watch": "ng build --watch --configuration development", "watch": "ng build --watch --configuration development",
"test": "ng test" "test": "ng test"
@@ -19,13 +19,16 @@
"@angular/material": "^22.0.4", "@angular/material": "^22.0.4",
"@angular/platform-browser": "^22.0.0", "@angular/platform-browser": "^22.0.0",
"@angular/router": "^22.0.0", "@angular/router": "^22.0.0",
"ngx-cookie-service": "^22.0.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0" "tslib": "^2.3.0",
"uuid": "^14.0.1"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "^22.0.6", "@angular/build": "^22.0.6",
"@angular/cli": "^22.0.6", "@angular/cli": "^22.0.6",
"@angular/compiler-cli": "^22.0.0", "@angular/compiler-cli": "^22.0.0",
"@openapitools/openapi-generator-cli": "^2.40.0",
"jsdom": "^28.0.0", "jsdom": "^28.0.0",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"typescript": "~6.0.2", "typescript": "~6.0.2",
+2 -4
View File
@@ -2,10 +2,8 @@ import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/
import { provideRouter } from '@angular/router'; import { provideRouter } from '@angular/router';
import { routes } from './app.routes'; import { routes } from './app.routes';
import { CookieService } from 'ngx-cookie-service';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [provideBrowserGlobalErrorListeners(), provideRouter(routes), CookieService]
provideBrowserGlobalErrorListeners(),
provideRouter(routes)
]
}; };
+3
View File
@@ -0,0 +1,3 @@
.example-spacer {
flex: 1 1 auto;
}
+5 -344
View File
@@ -1,344 +1,5 @@
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * --> <mat-tab-group mat-stretch-tabs="false" mat-align-tabs="center">
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * --> <mat-tab label="First">Content 1</mat-tab>
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * --> <mat-tab label="Second">Content 2</mat-tab>
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * --> <mat-tab label="Third">Content 3</mat-tab>
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * --> </mat-tab-group>
<!-- * * * * * * * * * Delete the template below * * * * * * * * * -->
<!-- * * * * * * * to get started with your project! * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<style>
:host {
--bright-blue: oklch(51.01% 0.274 263.83);
--electric-violet: oklch(53.18% 0.28 296.97);
--french-violet: oklch(47.66% 0.246 305.88);
--vivid-pink: oklch(69.02% 0.277 332.77);
--hot-red: oklch(61.42% 0.238 15.34);
--orange-red: oklch(63.32% 0.24 31.68);
--gray-900: oklch(19.37% 0.006 300.98);
--gray-700: oklch(36.98% 0.014 302.71);
--gray-400: oklch(70.9% 0.015 304.04);
--red-to-pink-to-purple-vertical-gradient: linear-gradient(
180deg,
var(--orange-red) 0%,
var(--vivid-pink) 50%,
var(--electric-violet) 100%
);
--red-to-pink-to-purple-horizontal-gradient: linear-gradient(
90deg,
var(--orange-red) 0%,
var(--vivid-pink) 50%,
var(--electric-violet) 100%
);
--pill-accent: var(--bright-blue);
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: block;
height: 100dvh;
}
h1 {
font-size: 3.125rem;
color: var(--gray-900);
font-weight: 500;
line-height: 100%;
letter-spacing: -0.125rem;
margin: 0;
font-family: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
}
p {
margin: 0;
color: var(--gray-700);
}
main {
width: 100%;
min-height: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
box-sizing: inherit;
position: relative;
}
.angular-logo {
max-width: 9.2rem;
}
.content {
display: flex;
justify-content: space-around;
width: 100%;
max-width: 700px;
margin-bottom: 3rem;
}
.content h1 {
margin-top: 1.75rem;
}
.content p {
margin-top: 1.5rem;
}
.divider {
width: 1px;
background: var(--red-to-pink-to-purple-vertical-gradient);
margin-inline: 0.5rem;
}
.pill-group {
display: flex;
flex-direction: column;
align-items: start;
flex-wrap: wrap;
gap: 1.25rem;
}
.pill {
display: flex;
align-items: center;
--pill-accent: var(--bright-blue);
background: color-mix(in srgb, var(--pill-accent) 5%, transparent);
color: var(--pill-accent);
padding-inline: 0.75rem;
padding-block: 0.375rem;
border-radius: 2.75rem;
border: 0;
transition: background 0.3s ease;
font-family: var(--inter-font);
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1.4rem;
letter-spacing: -0.00875rem;
text-decoration: none;
white-space: nowrap;
}
.pill:hover {
background: color-mix(in srgb, var(--pill-accent) 15%, transparent);
}
.pill-group .pill:nth-child(6n + 1) {
--pill-accent: var(--bright-blue);
}
.pill-group .pill:nth-child(6n + 2) {
--pill-accent: var(--electric-violet);
}
.pill-group .pill:nth-child(6n + 3) {
--pill-accent: var(--french-violet);
}
.pill-group .pill:nth-child(6n + 4),
.pill-group .pill:nth-child(6n + 5),
.pill-group .pill:nth-child(6n + 6) {
--pill-accent: var(--hot-red);
}
.pill-group svg {
margin-inline-start: 0.25rem;
}
.social-links {
display: flex;
align-items: center;
gap: 0.73rem;
margin-top: 1.5rem;
}
.social-links path {
transition: fill 0.3s ease;
fill: var(--gray-400);
}
.social-links a:hover svg path {
fill: var(--gray-900);
}
@media screen and (max-width: 650px) {
.content {
flex-direction: column;
width: max-content;
}
.divider {
height: 1px;
width: 100%;
background: var(--red-to-pink-to-purple-horizontal-gradient);
margin-block: 1.5rem;
}
}
</style>
<main class="main">
<div class="content">
<div class="left-side">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 982 239"
fill="none"
class="angular-logo"
>
<g clip-path="url(#a)">
<path
fill="url(#b)"
d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.602 64.311h-44.431Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.039-7.037 7.037 0 12.846 1.145 17.347 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.745 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.618 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.009 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.092 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.814 58.814 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
/>
<path
fill="url(#c)"
d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.602 64.311h-44.431Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.039-7.037 7.037 0 12.846 1.145 17.347 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.745 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.618 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.009 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.092 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.814 58.814 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
/>
</g>
<defs>
<radialGradient
id="c"
cx="0"
cy="0"
r="1"
gradientTransform="rotate(118.122 171.182 60.81) scale(205.794)"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#FF41F8" />
<stop offset=".707" stop-color="#FF41F8" stop-opacity=".5" />
<stop offset="1" stop-color="#FF41F8" stop-opacity="0" />
</radialGradient>
<linearGradient
id="b"
x1="0"
x2="982"
y1="192"
y2="192"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F0060B" />
<stop offset="0" stop-color="#F0070C" />
<stop offset=".526" stop-color="#CC26D5" />
<stop offset="1" stop-color="#7702FF" />
</linearGradient>
<clipPath id="a"><path fill="#fff" d="M0 0h982v239H0z" /></clipPath>
</defs>
</svg>
<h1>Hello, {{ title() }}</h1>
<p>Congratulations! Your app is running. 🎉</p>
</div>
<div class="divider" role="separator" aria-label="Divider"></div>
<div class="right-side">
<div class="pill-group">
@for (item of [
{ title: 'Explore the Docs', link: 'https://angular.dev' },
{ title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' },
{ title: 'Prompt and best practices for AI', link: 'https://angular.dev/ai/develop-with-ai'},
{ title: 'CLI Docs', link: 'https://angular.dev/tools/cli' },
{ title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' },
{ title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' },
]; track item.title) {
<a
class="pill"
[href]="item.link"
target="_blank"
rel="noopener"
>
<span>{{ item.title }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
height="14"
viewBox="0 -960 960 960"
width="14"
fill="currentColor"
>
<path
d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h560v-280h80v280q0 33-23.5 56.5T760-120H200Zm188-212-56-56 372-372H560v-80h280v280h-80v-144L388-332Z"
/>
</svg>
</a>
}
</div>
<div class="social-links">
<a
href="https://github.com/angular/angular"
aria-label="Github"
target="_blank"
rel="noopener"
>
<svg
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
alt="Github"
>
<path
d="M12.3047 0C5.50634 0 0 5.50942 0 12.3047C0 17.7423 3.52529 22.3535 8.41332 23.9787C9.02856 24.0946 9.25414 23.7142 9.25414 23.3871C9.25414 23.0949 9.24389 22.3207 9.23876 21.2953C5.81601 22.0377 5.09414 19.6444 5.09414 19.6444C4.53427 18.2243 3.72524 17.8449 3.72524 17.8449C2.61064 17.082 3.81137 17.0973 3.81137 17.0973C5.04697 17.1835 5.69604 18.3647 5.69604 18.3647C6.79321 20.2463 8.57636 19.7029 9.27978 19.3881C9.39052 18.5924 9.70736 18.0499 10.0591 17.7423C7.32641 17.4347 4.45429 16.3765 4.45429 11.6618C4.45429 10.3185 4.9311 9.22133 5.72065 8.36C5.58222 8.04931 5.16694 6.79833 5.82831 5.10337C5.82831 5.10337 6.85883 4.77319 9.2121 6.36459C10.1965 6.09082 11.2424 5.95546 12.2883 5.94931C13.3342 5.95546 14.3801 6.09082 15.3644 6.36459C17.7023 4.77319 18.7328 5.10337 18.7328 5.10337C19.3942 6.79833 18.9789 8.04931 18.8559 8.36C19.6403 9.22133 20.1171 10.3185 20.1171 11.6618C20.1171 16.3888 17.2409 17.4296 14.5031 17.7321C14.9338 18.1012 15.3337 18.8559 15.3337 20.0084C15.3337 21.6552 15.3183 22.978 15.3183 23.3779C15.3183 23.7009 15.5336 24.0854 16.1642 23.9623C21.0871 22.3484 24.6094 17.7341 24.6094 12.3047C24.6094 5.50942 19.0999 0 12.3047 0Z"
/>
</svg>
</a>
<a
href="https://x.com/angular"
aria-label="X"
target="_blank"
rel="noopener"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
alt="X"
>
<path
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
/>
</svg>
</a>
<a
href="https://www.youtube.com/channel/UCbn1OgGei-DV7aSRo_HaAiw"
aria-label="Youtube"
target="_blank"
rel="noopener"
>
<svg
width="29"
height="20"
viewBox="0 0 29 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
alt="Youtube"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M27.4896 1.52422C27.9301 1.96749 28.2463 2.51866 28.4068 3.12258C29.0004 5.35161 29.0004 10 29.0004 10C29.0004 10 29.0004 14.6484 28.4068 16.8774C28.2463 17.4813 27.9301 18.0325 27.4896 18.4758C27.0492 18.9191 26.5 19.2389 25.8972 19.4032C23.6778 20 14.8068 20 14.8068 20C14.8068 20 5.93586 20 3.71651 19.4032C3.11363 19.2389 2.56449 18.9191 2.12405 18.4758C1.68361 18.0325 1.36732 17.4813 1.20683 16.8774C0.613281 14.6484 0.613281 10 0.613281 10C0.613281 10 0.613281 5.35161 1.20683 3.12258C1.36732 2.51866 1.68361 1.96749 2.12405 1.52422C2.56449 1.08095 3.11363 0.76113 3.71651 0.596774C5.93586 0 14.8068 0 14.8068 0C14.8068 0 23.6778 0 25.8972 0.596774C26.5 0.76113 27.0492 1.08095 27.4896 1.52422ZM19.3229 10L11.9036 5.77905V14.221L19.3229 10Z"
/>
</svg>
</a>
</div>
</div>
</div>
</main>
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * * -->
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * * -->
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<router-outlet />
+57 -8
View File
@@ -1,16 +1,65 @@
import { Component, signal } from '@angular/core'; import { Component, OnInit, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatTabsModule } from '@angular/material/tabs';
import { HttpClient } from '@angular/common/http';
import { CookieService } from 'ngx-cookie-service';
import { v4 as uuidv4 } from 'uuid';
import { YoutubeSearchResult } from './youtube-search-result/youtube-search-result';
import { Session, SessionControllerService } from './core/api';
@Component({ @Component({
standalone: true,
selector: 'app-root', selector: 'app-root',
imports: [RouterOutlet, MatButtonModule, MatIconModule], imports: [MatButtonModule, MatIconModule, MatTabsModule, YoutubeSearchResult],
templateUrl: './app.html', template: `
<mat-tab-group mat-stretch-tabs="false" mat-align-tabs="center">
<mat-tab label="Session"></mat-tab>
<mat-tab label="My Queue"></mat-tab>
<mat-tab label="Search"></mat-tab>
</mat-tab-group>
<app-youtube-search-result></app-youtube-search-result>
<app-youtube-search-result></app-youtube-search-result>
<app-youtube-search-result></app-youtube-search-result>
<app-youtube-search-result></app-youtube-search-result>
<app-youtube-search-result></app-youtube-search-result>
<app-youtube-search-result></app-youtube-search-result>
`,
styleUrl: './app.css', styleUrl: './app.css',
}) })
export class App { export class App implements OnInit {
protected readonly title = signal('frontend'); private _cookieService;
private _sessionService;
private userIdCookieName = 'userId';
constructor(
private http: HttpClient,
private cookieService: CookieService,
private sessionControllerService: SessionControllerService,
) {
this._cookieService = cookieService;
this._sessionService = sessionControllerService;
}
ngOnInit(): void {
let name = this._cookieService.get(this.userIdCookieName);
console.log(name);
if (name == null || name == '') {
this._cookieService.set(this.userIdCookieName, uuidv4(), 365);
}
this._sessionService.getSession().subscribe({
next: (session: Session) => {
// Here is your Session variable!
console.log(session);
console.log(session.sessionId);
},
error: (error) => {
console.error('Request failed', error);
},
});
}
} }
+4
View File
@@ -0,0 +1,4 @@
wwwroot/*.js
node_modules
typings
dist
@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
@@ -0,0 +1,31 @@
.gitignore
README.md
api.base.service.ts
api.module.ts
api/api.ts
api/clientController.service.ts
api/healthController.service.ts
api/playerController.service.ts
api/sessionController.service.ts
api/userController.service.ts
api/youtubeController.service.ts
configuration.ts
encoder.ts
git_push.sh
index.ts
model/album.ts
model/artist.ts
model/client.ts
model/clientRegistrationRequestDto.ts
model/createUserRequest.ts
model/models.ts
model/musicRecord.ts
model/queryDto.ts
model/session.ts
model/sessionQueueElement.ts
model/thumbnails.ts
model/user.ts
param.ts
provide-api.ts
query.params.ts
variables.ts
@@ -0,0 +1 @@
7.24.0
+185
View File
@@ -0,0 +1,185 @@
# @
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
The version of the OpenAPI document: v0
## Building
To install the required dependencies and to build the typescript sources run:
```console
npm install
npm run build
```
## Publishing
First build the package then run `npm publish dist` (don't forget to specify the `dist` folder!)
## Consuming
Navigate to the folder of your consuming project and run one of next commands.
_published:_
```console
npm install @ --save
```
_without publishing (not recommended):_
```console
npm install PATH_TO_GENERATED_PACKAGE/dist.tgz --save
```
_It's important to take the tgz file, otherwise you'll get trouble with links on windows_
_using `npm link`:_
In PATH_TO_GENERATED_PACKAGE/dist:
```console
npm link
```
In your project:
```console
npm link
```
__Note for Windows users:__ The Angular CLI has troubles to use linked npm packages.
Please refer to this issue <https://github.com/angular/angular-cli/issues/8284> for a solution / workaround.
Published packages are not effected by this issue.
### General usage
In your Angular project:
```typescript
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
import { provideApi } from '';
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideHttpClient(),
provideApi()
],
};
```
**NOTE**
If you're still using `AppModule` and haven't [migrated](https://angular.dev/reference/migrations/standalone) yet, you can still import an Angular module:
```typescript
import { ApiModule } from '';
```
If different from the generated base path, during app bootstrap, you can provide the base path to your service.
```typescript
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
import { provideApi } from '';
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideHttpClient(),
provideApi('http://localhost:9999')
],
};
```
```typescript
// with a custom configuration
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
import { provideApi } from '';
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideHttpClient(),
provideApi({
withCredentials: true,
username: 'user',
password: 'password'
})
],
};
```
```typescript
// with factory building a custom configuration
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
import { provideApi, Configuration } from '';
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideHttpClient(),
{
provide: Configuration,
useFactory: (authService: AuthService) => new Configuration({
basePath: 'http://localhost:9999',
withCredentials: true,
username: authService.getUsername(),
password: authService.getPassword(),
}),
deps: [AuthService],
multi: false
}
],
};
```
### Using multiple OpenAPI files / APIs
In order to use multiple APIs generated from different OpenAPI files,
you can create an alias name when importing the modules
in order to avoid naming conflicts:
```typescript
import { provideApi as provideUserApi } from 'my-user-api-path';
import { provideApi as provideAdminApi } from 'my-admin-api-path';
import { HttpClientModule } from '@angular/common/http';
import { environment } from '../environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideHttpClient(),
provideUserApi(environment.basePath),
provideAdminApi(environment.basePath),
],
};
```
### Customizing path parameter encoding
Without further customization, only [path-parameters][parameter-locations-url] of [style][style-values-url] 'simple'
and Dates for format 'date-time' are encoded correctly.
Other styles (e.g. "matrix") are not that easy to encode
and thus are best delegated to other libraries (e.g.: [@honoluluhenk/http-param-expander]).
To implement your own parameter encoding (or call another library),
pass an arrow-function or method-reference to the `encodeParam` property of the Configuration-object
(see [General Usage](#general-usage) above).
Example value for use in your Configuration-Provider:
```typescript
new Configuration({
encodeParam: (param: Param) => myFancyParamEncoder(param),
})
```
[parameter-locations-url]: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-locations
[style-values-url]: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-values
[@honoluluhenk/http-param-expander]: https://www.npmjs.com/package/@honoluluhenk/http-param-expander
@@ -0,0 +1,96 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { HttpHeaders, HttpParams, HttpParameterCodec } from '@angular/common/http';
import { CustomHttpParameterCodec } from './encoder';
import { Configuration } from './configuration';
import { OpenApiHttpParams, QueryParamStyle, concatHttpParamsObject} from './query.params';
export class BaseService {
protected basePath = 'http://localhost:8080';
public defaultHeaders = new HttpHeaders();
public configuration: Configuration;
public encoder: HttpParameterCodec;
constructor(basePath?: string|string[], configuration?: Configuration) {
this.configuration = configuration || new Configuration();
if (typeof this.configuration.basePath !== 'string') {
const firstBasePath = Array.isArray(basePath) ? basePath[0] : undefined;
if (firstBasePath != undefined) {
basePath = firstBasePath;
}
if (typeof basePath !== 'string') {
basePath = this.basePath;
}
this.configuration.basePath = basePath;
}
this.encoder = this.configuration.encoder || new CustomHttpParameterCodec();
}
protected canConsumeForm(consumes: string[]): boolean {
return consumes.indexOf('multipart/form-data') !== -1;
}
protected addToHttpParams(httpParams: OpenApiHttpParams, key: string, value: any | null | undefined, paramStyle: QueryParamStyle, explode: boolean): OpenApiHttpParams {
if (value === null || value === undefined) {
return httpParams;
}
if (paramStyle === QueryParamStyle.DeepObject) {
if (typeof value !== 'object') {
throw Error(`An object must be provided for key ${key} as it is a deep object`);
}
return Object.keys(value as Record<string, any>).reduce(
(hp, k) => hp.append(`${key}[${k}]`, value[k]),
httpParams,
);
} else if (paramStyle === QueryParamStyle.Json) {
return httpParams.append(key, JSON.stringify(value));
} else {
// Form-style, SpaceDelimited or PipeDelimited
if (Object(value) !== value) {
// If it is a primitive type, add its string representation
return httpParams.append(key, value.toString());
} else if (value instanceof Date) {
return httpParams.append(key, value.toISOString());
} else if (Array.isArray(value) || value instanceof Set) {
// Otherwise, if it's an array or set, add each element.
const array = Array.isArray(value) ? value : Array.from(value);
if (paramStyle === QueryParamStyle.Form) {
return httpParams.set(key, array, {explode: explode, delimiter: ','});
} else if (paramStyle === QueryParamStyle.SpaceDelimited) {
return httpParams.set(key, array, {explode: explode, delimiter: ' '});
} else {
// PipeDelimited
return httpParams.set(key, array, {explode: explode, delimiter: '|'});
}
} else {
// Otherwise, if it's an object, add each field.
if (paramStyle === QueryParamStyle.Form) {
if (explode) {
Object.keys(value).forEach(k => {
httpParams = this.addToHttpParams(httpParams, k, value[k], paramStyle, explode);
});
return httpParams;
} else {
return concatHttpParamsObject(httpParams, key, value, ',');
}
} else if (paramStyle === QueryParamStyle.SpaceDelimited) {
return concatHttpParamsObject(httpParams, key, value, ' ');
} else {
// PipeDelimited
return concatHttpParamsObject(httpParams, key, value, '|');
}
}
}
}
}
+30
View File
@@ -0,0 +1,30 @@
import { NgModule, ModuleWithProviders, SkipSelf, Optional } from '@angular/core';
import { Configuration } from './configuration';
import { HttpClient } from '@angular/common/http';
@NgModule({
imports: [],
declarations: [],
exports: [],
providers: []
})
export class ApiModule {
public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders<ApiModule> {
return {
ngModule: ApiModule,
providers: [ { provide: Configuration, useFactory: configurationFactory } ]
};
}
constructor( @Optional() @SkipSelf() parentModule: ApiModule,
@Optional() http: HttpClient) {
if (parentModule) {
throw new Error('ApiModule is already loaded. Import in your base AppModule only.');
}
if (!http) {
throw new Error('You need to import the HttpClientModule in your AppModule! \n' +
'See also https://github.com/angular/angular/issues/20575');
}
}
}
+13
View File
@@ -0,0 +1,13 @@
export * from './clientController.service';
import { ClientControllerService } from './clientController.service';
export * from './healthController.service';
import { HealthControllerService } from './healthController.service';
export * from './playerController.service';
import { PlayerControllerService } from './playerController.service';
export * from './sessionController.service';
import { SessionControllerService } from './sessionController.service';
export * from './userController.service';
import { UserControllerService } from './userController.service';
export * from './youtubeController.service';
import { YoutubeControllerService } from './youtubeController.service';
export const APIS = [ClientControllerService, HealthControllerService, PlayerControllerService, SessionControllerService, UserControllerService, YoutubeControllerService];
@@ -0,0 +1,105 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/* tslint:disable:no-unused-variable member-ordering */
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent, HttpContext
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { OpenApiHttpParams, QueryParamStyle } from '../query.params';
// @ts-ignore
import { Client } from '../model/client';
// @ts-ignore
import { ClientRegistrationRequestDto } from '../model/clientRegistrationRequestDto';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
import { BaseService } from '../api.base.service';
@Injectable({
providedIn: 'root'
})
export class ClientControllerService extends BaseService {
constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
super(basePath, configuration);
}
/**
* @endpoint post /api/client/register
* @param clientRegistrationRequestDto
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public registerClient(clientRegistrationRequestDto: ClientRegistrationRequestDto, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<Client>;
public registerClient(clientRegistrationRequestDto: ClientRegistrationRequestDto, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<Client>>;
public registerClient(clientRegistrationRequestDto: ClientRegistrationRequestDto, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<Client>>;
public registerClient(clientRegistrationRequestDto: ClientRegistrationRequestDto, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<any> {
if (clientRegistrationRequestDto === null || clientRegistrationRequestDto === undefined) {
throw new Error('Required parameter clientRegistrationRequestDto was null or undefined when calling registerClient.');
}
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'*/*'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
// to determine the Content-Type header
const consumes: string[] = [
'application/json'
];
const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
if (httpContentTypeSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
}
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
let localVarPath = `/api/client/register`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<Client>('post', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
body: clientRegistrationRequestDto,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
}
@@ -0,0 +1,87 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/* tslint:disable:no-unused-variable member-ordering */
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent, HttpContext
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { OpenApiHttpParams, QueryParamStyle } from '../query.params';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
import { BaseService } from '../api.base.service';
@Injectable({
providedIn: 'root'
})
export class HealthControllerService extends BaseService {
constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
super(basePath, configuration);
}
/**
* @endpoint get /health/ping
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public ping(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<string>;
public ping(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<string>>;
public ping(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<string>>;
public ping(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<any> {
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'*/*'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
let localVarPath = `/health/ping`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<string>('get', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
}
@@ -0,0 +1,91 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/* tslint:disable:no-unused-variable member-ordering */
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent, HttpContext
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { OpenApiHttpParams, QueryParamStyle } from '../query.params';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
import { BaseService } from '../api.base.service';
@Injectable({
providedIn: 'root'
})
export class PlayerControllerService extends BaseService {
constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
super(basePath, configuration);
}
/**
* @endpoint post /api/player/play/{id}
* @param id
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public play(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<string>;
public play(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<string>>;
public play(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<string>>;
public play(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<any> {
if (id === null || id === undefined) {
throw new Error('Required parameter id was null or undefined when calling play.');
}
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'*/*'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
let localVarPath = `/api/player/play/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<string>('post', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
}
@@ -0,0 +1,256 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/* tslint:disable:no-unused-variable member-ordering */
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent, HttpContext
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { OpenApiHttpParams, QueryParamStyle } from '../query.params';
// @ts-ignore
import { Session } from '../model/session';
// @ts-ignore
import { User } from '../model/user';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
import { BaseService } from '../api.base.service';
@Injectable({
providedIn: 'root'
})
export class SessionControllerService extends BaseService {
constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
super(basePath, configuration);
}
/**
* @endpoint post /session/music/add/{musicId}
* @param authorization
* @param musicId
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public addMusic(authorization: string, musicId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<User>;
public addMusic(authorization: string, musicId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<User>>;
public addMusic(authorization: string, musicId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<User>>;
public addMusic(authorization: string, musicId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<any> {
if (authorization === null || authorization === undefined) {
throw new Error('Required parameter authorization was null or undefined when calling addMusic.');
}
if (musicId === null || musicId === undefined) {
throw new Error('Required parameter musicId was null or undefined when calling addMusic.');
}
let localVarHeaders = this.defaultHeaders;
if (authorization !== undefined && authorization !== null) {
localVarHeaders = localVarHeaders.set('Authorization', String(authorization));
}
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'*/*'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
let localVarPath = `/session/music/add/${this.configuration.encodeParam({name: "musicId", value: musicId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<User>('post', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
/**
* @endpoint get /session
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public getSession(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable<Session>;
public getSession(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<Session>>;
public getSession(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<Session>>;
public getSession(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable<any> {
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'application/json'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
console.log(responseType_)
let localVarPath = `/session`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<Session>('get', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
/**
* @endpoint get /session/testing/next/user
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public nextUser(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<Session>;
public nextUser(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<Session>>;
public nextUser(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<Session>>;
public nextUser(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<any> {
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'*/*'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
let localVarPath = `/session/testing/next/user`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<Session>('get', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
/**
* @endpoint get /session/testing/play/next
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public playNext(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<Session>;
public playNext(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<Session>>;
public playNext(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<Session>>;
public playNext(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<any> {
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'*/*'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
let localVarPath = `/session/testing/play/next`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<Session>('get', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
}
@@ -0,0 +1,105 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/* tslint:disable:no-unused-variable member-ordering */
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent, HttpContext
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { OpenApiHttpParams, QueryParamStyle } from '../query.params';
// @ts-ignore
import { CreateUserRequest } from '../model/createUserRequest';
// @ts-ignore
import { User } from '../model/user';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
import { BaseService } from '../api.base.service';
@Injectable({
providedIn: 'root'
})
export class UserControllerService extends BaseService {
constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
super(basePath, configuration);
}
/**
* @endpoint post /api/user
* @param createUserRequest
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public createUser(createUserRequest: CreateUserRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<Array<User>>;
public createUser(createUserRequest: CreateUserRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<Array<User>>>;
public createUser(createUserRequest: CreateUserRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<Array<User>>>;
public createUser(createUserRequest: CreateUserRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<any> {
if (createUserRequest === null || createUserRequest === undefined) {
throw new Error('Required parameter createUserRequest was null or undefined when calling createUser.');
}
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'*/*'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
// to determine the Content-Type header
const consumes: string[] = [
'application/json'
];
const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
if (httpContentTypeSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
}
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
let localVarPath = `/api/user`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<Array<User>>('post', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
body: createUserRequest,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
}
@@ -0,0 +1,105 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/* tslint:disable:no-unused-variable member-ordering */
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent, HttpContext
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { OpenApiHttpParams, QueryParamStyle } from '../query.params';
// @ts-ignore
import { MusicRecord } from '../model/musicRecord';
// @ts-ignore
import { QueryDto } from '../model/queryDto';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
import { BaseService } from '../api.base.service';
@Injectable({
providedIn: 'root'
})
export class YoutubeControllerService extends BaseService {
constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
super(basePath, configuration);
}
/**
* @endpoint post /api/youtube/query
* @param queryDto
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
public createProduct(queryDto: QueryDto, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<Array<MusicRecord>>;
public createProduct(queryDto: QueryDto, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<Array<MusicRecord>>>;
public createProduct(queryDto: QueryDto, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<Array<MusicRecord>>>;
public createProduct(queryDto: QueryDto, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: '*/*', context?: HttpContext, transferCache?: boolean}): Observable<any> {
if (queryDto === null || queryDto === undefined) {
throw new Error('Required parameter queryDto was null or undefined when calling createProduct.');
}
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
'*/*'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
// to determine the Content-Type header
const consumes: string[] = [
'application/json'
];
const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
if (httpContentTypeSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
}
let responseType_: 'text' | 'json' | 'blob' = 'json';
if (localVarHttpHeaderAcceptSelected) {
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
responseType_ = 'text';
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
responseType_ = 'json';
} else {
responseType_ = 'blob';
}
}
let localVarPath = `/api/youtube/query`;
const { basePath, withCredentials } = this.configuration;
return this.httpClient.request<Array<MusicRecord>>('post', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
body: queryDto,
responseType: <any>responseType_,
...(withCredentials ? { withCredentials } : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
reportProgress: reportProgress
}
);
}
}
+185
View File
@@ -0,0 +1,185 @@
import { HttpHeaders, HttpParameterCodec } from '@angular/common/http';
import { Param } from './param';
import { OpenApiHttpParams } from './query.params';
export interface ConfigurationParameters {
/**
* @deprecated Since 5.0. Use credentials instead
*/
apiKeys?: {[ key: string ]: string};
username?: string;
password?: string;
/**
* @deprecated Since 5.0. Use credentials instead
*/
accessToken?: string | (() => string);
basePath?: string;
withCredentials?: boolean;
/**
* Takes care of encoding query- and form-parameters.
*/
encoder?: HttpParameterCodec;
/**
* Override the default method for encoding path parameters in various
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-values">styles</a>.
* <p>
* See {@link README.md} for more details
* </p>
*/
encodeParam?: (param: Param) => string;
/**
* The keys are the names in the securitySchemes section of the OpenAPI
* document. They should map to the value used for authentication
* minus any standard prefixes such as 'Basic' or 'Bearer'.
*/
credentials?: {[ key: string ]: string | (() => string | undefined)};
}
export class Configuration {
/**
* @deprecated Since 5.0. Use credentials instead
*/
apiKeys?: {[ key: string ]: string};
username?: string;
password?: string;
/**
* @deprecated Since 5.0. Use credentials instead
*/
accessToken?: string | (() => string);
basePath?: string;
withCredentials?: boolean;
/**
* Takes care of encoding query- and form-parameters.
*/
encoder?: HttpParameterCodec;
/**
* Encoding of various path parameter
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-values">styles</a>.
* <p>
* See {@link README.md} for more details
* </p>
*/
encodeParam: (param: Param) => string;
/**
* The keys are the names in the securitySchemes section of the OpenAPI
* document. They should map to the value used for authentication
* minus any standard prefixes such as 'Basic' or 'Bearer'.
*/
credentials: {[ key: string ]: string | (() => string | undefined)};
constructor({ accessToken, apiKeys, basePath, credentials, encodeParam, encoder, password, username, withCredentials }: ConfigurationParameters = {}) {
if (apiKeys) {
this.apiKeys = apiKeys;
}
if (username !== undefined) {
this.username = username;
}
if (password !== undefined) {
this.password = password;
}
if (accessToken !== undefined) {
this.accessToken = accessToken;
}
if (basePath !== undefined) {
this.basePath = basePath;
}
if (withCredentials !== undefined) {
this.withCredentials = withCredentials;
}
if (encoder) {
this.encoder = encoder;
}
this.encodeParam = encodeParam ?? (param => this.defaultEncodeParam(param));
this.credentials = credentials ?? {};
}
/**
* Select the correct content-type to use for a request.
* Uses {@link Configuration#isJsonMime} to determine the correct content-type.
* If no content type is found return the first found type if the contentTypes is not empty
* @param contentTypes - the array of content types that are available for selection
* @returns the selected content-type or <code>undefined</code> if no selection could be made.
*/
public selectHeaderContentType (contentTypes: string[]): string | undefined {
if (contentTypes.length === 0) {
return undefined;
}
const type = contentTypes.find((x: string) => this.isJsonMime(x));
if (type === undefined) {
return contentTypes[0];
}
return type;
}
/**
* Select the correct accept content-type to use for a request.
* Uses {@link Configuration#isJsonMime} to determine the correct accept content-type.
* If no content type is found return the first found type if the contentTypes is not empty
* @param accepts - the array of content types that are available for selection.
* @returns the selected content-type or <code>undefined</code> if no selection could be made.
*/
public selectHeaderAccept(accepts: string[]): string | undefined {
if (accepts.length === 0) {
return undefined;
}
const type = accepts.find((x: string) => this.isJsonMime(x));
if (type === undefined) {
return accepts[0];
}
return type;
}
/**
* Check if the given MIME is a JSON MIME.
* JSON MIME examples:
* application/json
* application/json; charset=UTF8
* APPLICATION/JSON
* application/vnd.company+json
* @param mime - MIME (Multipurpose Internet Mail Extensions)
* @return True if the given MIME is JSON, false otherwise.
*/
public isJsonMime(mime: string): boolean {
const jsonMime: RegExp = /^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$/i;
return mime !== null && jsonMime.test(mime);
}
public lookupCredential(key: string): string | undefined {
const value = this.credentials[key];
return typeof value === 'function'
? value()
: value;
}
public addCredentialToHeaders(credentialKey: string, headerName: string, headers: HttpHeaders, prefix?: string): HttpHeaders {
const value = this.lookupCredential(credentialKey);
return value
? headers.set(headerName, (prefix ?? '') + value)
: headers;
}
public addCredentialToQuery(credentialKey: string, paramName: string, query: OpenApiHttpParams): OpenApiHttpParams {
const value = this.lookupCredential(credentialKey);
return value
? query.set(paramName, value)
: query;
}
private defaultEncodeParam(param: Param): string {
// This implementation exists as fallback for missing configuration
// and for backwards compatibility to older typescript-angular generator versions.
// It only works for the 'simple' parameter style.
// Date-handling only works for the 'date-time' format.
// All other styles and Date-formats are probably handled incorrectly.
//
// But: if that's all you need (i.e.: the most common use-case): no need for customization!
const value = param.dataFormat === 'date-time' && param.value instanceof Date
? (param.value as Date).toISOString()
: param.value;
return encodeURIComponent(String(value));
}
}
+35
View File
@@ -0,0 +1,35 @@
import { HttpParameterCodec } from '@angular/common/http';
/**
* Custom HttpParameterCodec
* Workaround for https://github.com/angular/angular/issues/18261
*/
export class CustomHttpParameterCodec implements HttpParameterCodec {
encodeKey(k: string): string {
return encodeURIComponent(k);
}
encodeValue(v: string): string {
return encodeURIComponent(v);
}
decodeKey(k: string): string {
return decodeURIComponent(k);
}
decodeValue(v: string): string {
return decodeURIComponent(v);
}
}
export class IdentityHttpParameterCodec implements HttpParameterCodec {
encodeKey(k: string): string {
return k;
}
encodeValue(v: string): string {
return v;
}
decodeKey(k: string): string {
return k;
}
decodeValue(v: string): string {
return v;
}
}
+57
View File
@@ -0,0 +1,57 @@
#!/bin/sh
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
#
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
git_user_id=$1
git_repo_id=$2
release_note=$3
git_host=$4
if [ "$git_host" = "" ]; then
git_host="github.com"
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
fi
if [ "$git_user_id" = "" ]; then
git_user_id="GIT_USER_ID"
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
fi
if [ "$git_repo_id" = "" ]; then
git_repo_id="GIT_REPO_ID"
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
fi
if [ "$release_note" = "" ]; then
release_note="Minor update"
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
fi
# Initialize the local directory as a Git repository
git init
# Adds the files in the local repository and stages them for commit.
git add .
# Commits the tracked changes and prepares them to be pushed to a remote repository.
git commit -m "$release_note"
# Sets the new remote
git_remote=$(git remote)
if [ "$git_remote" = "" ]; then # git remote not defined
if [ "$GIT_TOKEN" = "" ]; then
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
else
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
fi
fi
git pull origin master
# Pushes (Forces) the changes in the local repository up to the remote repository
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
git push origin master 2>&1 | grep -v 'To https'
+7
View File
@@ -0,0 +1,7 @@
export * from './api/api';
export * from './model/models';
export * from './variables';
export * from './configuration';
export * from './api.module';
export * from './provide-api';
export * from './param';
+16
View File
@@ -0,0 +1,16 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface Album {
id?: string;
name?: string;
}
+16
View File
@@ -0,0 +1,16 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface Artist {
id?: string;
name?: string;
}
+16
View File
@@ -0,0 +1,16 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface Client {
id?: string;
host?: string;
}
@@ -0,0 +1,15 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface ClientRegistrationRequestDto {
host?: string;
}
@@ -0,0 +1,16 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface CreateUserRequest {
userId?: string;
nickName?: string;
}
+11
View File
@@ -0,0 +1,11 @@
export * from './album';
export * from './artist';
export * from './client';
export * from './clientRegistrationRequestDto';
export * from './createUserRequest';
export * from './musicRecord';
export * from './queryDto';
export * from './session';
export * from './sessionQueueElement';
export * from './thumbnails';
export * from './user';
@@ -0,0 +1,28 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { Artist } from './artist';
import { Album } from './album';
import { Thumbnails } from './thumbnails';
export interface MusicRecord {
category?: string;
resultType?: string;
title?: string;
videoId?: string;
duration_seconds?: string;
duration?: string;
year?: string;
views?: string;
album?: Album;
artists?: Array<Artist>;
thumbnails?: Array<Thumbnails>;
}
@@ -0,0 +1,34 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface QueryDto {
queryString?: string;
filter?: QueryDto.FilterEnum;
limit?: number;
}
export namespace QueryDto {
export const FilterEnum = {
Albums: 'ALBUMS',
Artists: 'ARTISTS',
Playlists: 'PLAYLISTS',
CommunityPlaylists: 'COMMUNITY_PLAYLISTS',
FeaturedPlalists: 'FEATURED_PLALISTS',
Videos: 'VIDEOS',
Profiles: 'PROFILES',
Podcasts: 'PODCASTS',
Episodes: 'EPISODES',
Songs: 'SONGS',
None: 'NONE'
} as const;
export type FilterEnum = typeof FilterEnum[keyof typeof FilterEnum];
}
@@ -0,0 +1,22 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { MusicRecord } from './musicRecord';
import { User } from './user';
import { SessionQueueElement } from './sessionQueueElement';
export interface Session {
sessionId?: string;
currentUserIndex?: number;
currentMusicRecord?: MusicRecord;
sessionQueue?: Array<SessionQueueElement>;
users?: Array<User>;
}
@@ -0,0 +1,17 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { MusicRecord } from './musicRecord';
export interface SessionQueueElement {
userName?: string;
musicRecord?: MusicRecord;
}
@@ -0,0 +1,17 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface Thumbnails {
url?: string;
width?: number;
height?: number;
}
+19
View File
@@ -0,0 +1,19 @@
/**
* OpenAPI definition
*
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { MusicRecord } from './musicRecord';
export interface User {
userId?: string;
nickName?: string;
popped?: Array<MusicRecord>;
musicQueue?: Array<MusicRecord>;
}
+69
View File
@@ -0,0 +1,69 @@
/**
* Standard parameter styles defined by OpenAPI spec
*/
export type StandardParamStyle =
| 'matrix'
| 'label'
| 'form'
| 'simple'
| 'spaceDelimited'
| 'pipeDelimited'
| 'deepObject'
;
/**
* The OpenAPI standard {@link StandardParamStyle}s may be extended by custom styles by the user.
*/
export type ParamStyle = StandardParamStyle | string;
/**
* Standard parameter locations defined by OpenAPI spec
*/
export type ParamLocation = 'query' | 'header' | 'path' | 'cookie';
/**
* Standard types as defined in <a href="https://swagger.io/specification/#data-types">OpenAPI Specification: Data Types</a>
*/
export type StandardDataType =
| "integer"
| "number"
| "boolean"
| "string"
| "object"
| "array"
;
/**
* Standard {@link DataType}s plus your own types/classes.
*/
export type DataType = StandardDataType | string;
/**
* Standard formats as defined in <a href="https://swagger.io/specification/#data-types">OpenAPI Specification: Data Types</a>
*/
export type StandardDataFormat =
| "int32"
| "int64"
| "float"
| "double"
| "byte"
| "binary"
| "date"
| "date-time"
| "password"
;
export type DataFormat = StandardDataFormat | string;
/**
* The parameter to encode.
*/
export interface Param {
name: string;
value: unknown;
in: ParamLocation;
style: ParamStyle,
explode: boolean;
dataType: DataType;
dataFormat: DataFormat | undefined;
}
+15
View File
@@ -0,0 +1,15 @@
import { EnvironmentProviders, makeEnvironmentProviders } from "@angular/core";
import { Configuration, ConfigurationParameters } from './configuration';
import { BASE_PATH } from './variables';
// Returns the service class providers, to be used in the [ApplicationConfig](https://angular.dev/api/core/ApplicationConfig).
export function provideApi(configOrBasePath: string | ConfigurationParameters): EnvironmentProviders {
return makeEnvironmentProviders([
typeof configOrBasePath === "string"
? { provide: BASE_PATH, useValue: configOrBasePath }
: {
provide: Configuration,
useValue: new Configuration({ ...configOrBasePath }),
},
]);
}
+160
View File
@@ -0,0 +1,160 @@
import { HttpParams, HttpParameterCodec } from '@angular/common/http';
import { CustomHttpParameterCodec, IdentityHttpParameterCodec } from './encoder';
export enum QueryParamStyle {
Json,
Form,
DeepObject,
SpaceDelimited,
PipeDelimited,
}
export type Delimiter = "," | " " | "|" | "\t";
export interface ParamOptions {
/** When true, serialized as multiple repeated key=value pairs. When false, serialized as a single key with joined values using `delimiter`. */
explode?: boolean;
/** Delimiter used when explode=false. The delimiter itself is inserted unencoded between encoded values. */
delimiter?: Delimiter;
}
interface ParamEntry {
values: string[];
options: Required<ParamOptions>;
}
export class OpenApiHttpParams {
private params: Map<string, ParamEntry> = new Map();
private defaults: Required<ParamOptions>;
private encoder: HttpParameterCodec;
/**
* @param encoder Parameter serializer
* @param defaults Global defaults used when a specific parameter has no explicit options.
* By OpenAPI default, explode is true for query params with style=form.
*/
constructor(encoder?: HttpParameterCodec, defaults?: { explode?: boolean; delimiter?: Delimiter }) {
this.encoder = encoder || new CustomHttpParameterCodec();
this.defaults = {
explode: defaults?.explode ?? true,
delimiter: defaults?.delimiter ?? ",",
};
}
private resolveOptions(local?: ParamOptions): Required<ParamOptions> {
return {
explode: local?.explode ?? this.defaults.explode,
delimiter: local?.delimiter ?? this.defaults.delimiter,
};
}
/**
* Replace the parameter's values and (optionally) its options.
* Options are stored per-parameter (not global).
*/
set(key: string, values: string[] | string, options?: ParamOptions): this {
const arr = Array.isArray(values) ? values.slice() : [values];
const opts = this.resolveOptions(options);
this.params.set(key, {values: arr, options: opts});
return this;
}
/**
* Append a single value to the parameter. If the parameter didn't exist it will be created
* and use resolved options (global defaults merged with any provided options).
*/
append(key: string, value: string, options?: ParamOptions): this {
const entry = this.params.get(key);
if (entry) {
// If new options provided, override the stored options for subsequent serialization
if (options) {
entry.options = this.resolveOptions({...entry.options, ...options});
}
entry.values.push(value);
} else {
this.set(key, [value], options);
}
return this;
}
/**
* Serialize to a query string according to per-parameter OpenAPI options.
* - If explode=true for that parameter → repeated key=value pairs (each value encoded).
* - If explode=false for that parameter → single key=value where values are individually encoded
* and joined using the configured delimiter. The delimiter character is inserted AS-IS
* (not percent-encoded).
*/
toString(): string {
const records = this.toRecord();
const parts: string[] = [];
for (const key in records) {
parts.push(`${key}=${records[key]}`);
}
return parts.join("&");
}
/**
* Return parameters as a plain record.
* - If a parameter has exactly one value, returns that value directly.
* - If a parameter has multiple values, returns a readonly array of values.
*/
toRecord(): Record<string, string | number | boolean | ReadonlyArray<string | number | boolean>> {
const parts: Record<string, string | number | boolean | ReadonlyArray<string | number | boolean>> = {};
for (const [key, entry] of this.params.entries()) {
const encodedKey = this.encoder.encodeKey(key);
if (entry.options.explode) {
parts[encodedKey] = entry.values.map((v) => this.encoder.encodeValue(v));
} else {
const encodedValues = entry.values.map((v) => this.encoder.encodeValue(v));
// join with the delimiter *unencoded*
parts[encodedKey] = encodedValues.join(entry.options.delimiter);
}
}
return parts;
}
/**
* Return an Angular's HttpParams with an identity parameter codec as the parameters are already encoded.
*/
toHttpParams(): HttpParams {
const records = this.toRecord();
let httpParams = new HttpParams({encoder: new IdentityHttpParameterCodec()});
return httpParams.appendAll(records);
}
}
export function concatHttpParamsObject(httpParams: OpenApiHttpParams, key: string, item: {
[index: string]: any
}, delimiter: Delimiter): OpenApiHttpParams {
let keyAndValues: string[] = [];
for (const k in item) {
keyAndValues.push(k);
const value = item[k];
if (Array.isArray(value)) {
keyAndValues.push(...value.map(convertToString));
} else {
keyAndValues.push(convertToString(value));
}
}
return httpParams.set(key, keyAndValues, {explode: false, delimiter: delimiter});
}
function convertToString(value: any): string {
if (value instanceof Date) {
return value.toISOString();
} else {
return value.toString();
}
}
+9
View File
@@ -0,0 +1,9 @@
import { InjectionToken } from '@angular/core';
export const BASE_PATH = new InjectionToken<string>('basePath');
export const COLLECTION_FORMATS = {
'csv': ',',
'tsv': ' ',
'ssv': ' ',
'pipes': '|'
}
@@ -0,0 +1,27 @@
<mat-nav-list>
@for (link of fragments; track link) {
<a
mat-list-item
href="#{{link}}"
(click)="$event.preventDefault(); activeLink.set(link)"
[activated]="activeLink() === link"
>{{link | titlecase}}</a
>
}
<h3 matSubheader>List with icons</h3>
@for (link of fragments; track link) {
<a
mat-list-item
[activated]="activeLink() === link"
href="#{{link}}"
(click)="$event.preventDefault(); activeLink.set(link);"
>
<mat-icon matListItemIcon>folder</mat-icon>
<span matListItemTitle>{{ link | titlecase}}</span>
<div matListItemMeta>
<mat-icon>{{link}}</mat-icon>
</div>
</a>
}
</mat-nav-list>
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserComponent } from './user.component';
describe('UserComponent', () => {
let component: UserComponent;
let fixture: ComponentFixture<UserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserComponent],
}).compileComponents();
fixture = TestBed.createComponent(UserComponent);
component = fixture.componentInstance;
await fixture.whenStable();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,15 @@
import { TitleCasePipe } from '@angular/common';
import { Component, signal } from '@angular/core';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
@Component({
selector: 'app-user',
imports: [MatListModule, MatIconModule, TitleCasePipe],
templateUrl: './user.component.html',
styleUrl: './user.component.css',
})
export class UserComponent {
fragments = ['inbox', 'outbox', 'drafts'];
activeLink = signal<string | null>(null);
}
@@ -0,0 +1,36 @@
.image-text-container {
display: flex; /* Aligns child elements side-by-side */
align-items: center; /* Centers the text vertically with the image */
gap: 20px; /* Creates a 20-pixel gap between the image and text */
}
.image-text-container-description {
display: flex;
flex-direction: column;
}
/* Optional: constrain image size so it doesn't get too large */
.image-text-container img {
width: 150px;
height: auto;
}
.yt-result {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
padding: 10px;
}
.yt-result_metadata {
font-size: small;
}
.yt-result-title {
font-weight: bold;
font-size: xx-large;
padding-bottom: 10px;
}
@@ -0,0 +1,14 @@
<div class="yt-result">
<div class="image-text-container">
<img src="https://yt3.googleusercontent.com/RFK4wHeGqwI3DndbARbRJB21IC0TcmqnrlyjxYK7T-nC8wlIVbfxNaCIFKNvSpchDKmYyVLe1RN36w=w120-h120-l90-rj"
width="60" height="60">
<div class="image-text-container-description">
<div class="yt-result-title">Abracadabra</div>
<div class="yt-result_metadata">Lady Gaga</div>
<div class="yt-result_metadata">MAYHEM</div>
<div class="yt-result_metadata">3:44</div>
<div class="yt-result_metadata">15M</div>
</div>
</div>
</div>
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { YoutubeSearchResult } from './youtube-search-result';
describe('YoutubeSearchResult', () => {
let component: YoutubeSearchResult;
let fixture: ComponentFixture<YoutubeSearchResult>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [YoutubeSearchResult],
}).compileComponents();
fixture = TestBed.createComponent(YoutubeSearchResult);
component = fixture.componentInstance;
await fixture.whenStable();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-youtube-search-result',
imports: [],
templateUrl: './youtube-search-result.html',
styleUrl: './youtube-search-result.css',
})
export class YoutubeSearchResult {}
-1
View File
@@ -1,5 +1,4 @@
FROM maven:3.9.16-amazoncorretto-25 AS builder FROM maven:3.9.16-amazoncorretto-25 AS builder
COPY settings.xml /usr/share/maven/conf/settings.xml
WORKDIR /build WORKDIR /build
COPY pom.xml . COPY pom.xml .
RUN mvn dependency:go-offline -B RUN mvn dependency:go-offline -B
-1
View File
@@ -1,5 +1,4 @@
FROM maven:3.9.16-amazoncorretto-25 AS builder FROM maven:3.9.16-amazoncorretto-25 AS builder
COPY settings.xml /usr/share/maven/conf/settings.xml
WORKDIR /build WORKDIR /build
COPY pom.xml . COPY pom.xml .
RUN mvn dependency:go-offline -B RUN mvn dependency:go-offline -B
+53
View File
@@ -59,6 +59,35 @@
<version>2.14.0</version> <version>2.14.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
<version>1.21.4</version>
</dependency>
<dependency>
<groupId>com.github.karsaig</groupId>
<artifactId>approvalcrest-junit-jupiter</artifactId>
<version>1.4.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.5</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -112,6 +141,30 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>cz.habarta.typescript-generator</groupId>
<artifactId>typescript-generator-maven-plugin</artifactId>
<version>4.1.1</version>
<executions>
<execution>
<id>generate-typescript</id>
<goals>
<goal>generate</goal>
</goals>
<phase>process-classes</phase>
</execution>
</executions>
<configuration>
<jsonLibrary>jackson2</jsonLibrary>
<outputKind>module</outputKind>
<outputFileType>implementationFile</outputFileType>
<mapClasses>asInterfaces</mapClasses>
<classes>
<class>zv.musix.entity.Session</class>
</classes>
<outputFile>../frontend/src/app/dto/api-types.ts</outputFile>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
@@ -0,0 +1,23 @@
package zv.musix.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*");
}
};
}
}
@@ -1,6 +1,7 @@
package zv.musix.controller; package zv.musix.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@@ -11,7 +12,7 @@ import zv.musix.entity.Client;
import zv.musix.service.ClientService; import zv.musix.service.ClientService;
@RestController @RestController
@RequestMapping("/api/client") @RequestMapping(value = "/api/client", produces = MediaType.APPLICATION_JSON_VALUE)
public class ClientController { public class ClientController {
@Autowired @Autowired
@@ -1,11 +1,12 @@
package zv.musix.controller; package zv.musix.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/health") @RequestMapping(value = "/health", produces = MediaType.APPLICATION_JSON_VALUE)
public class HealthController { public class HealthController {
@GetMapping("/ping") @GetMapping("/ping")
@@ -1,6 +1,7 @@
package zv.musix.controller; package zv.musix.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -10,7 +11,7 @@ import zv.musix.service.ClientService;
import zv.musix.service.PlayerService; import zv.musix.service.PlayerService;
@RestController @RestController
@RequestMapping("/api/player") @RequestMapping(value = "/api/player", produces = MediaType.APPLICATION_JSON_VALUE)
public class PlayerController { public class PlayerController {
@Autowired @Autowired
@@ -1,6 +1,7 @@
package zv.musix.controller; package zv.musix.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import zv.musix.entity.Session; import zv.musix.entity.Session;
@@ -8,7 +9,7 @@ import zv.musix.entity.User;
import zv.musix.service.SessionService; import zv.musix.service.SessionService;
@RestController @RestController
@RequestMapping("/session") @RequestMapping(value = "/session", produces = MediaType.APPLICATION_JSON_VALUE)
public class SessionController { public class SessionController {
@Autowired @Autowired
@@ -20,8 +21,8 @@ public class SessionController {
return ResponseEntity.ok(sessionService.getSession()); return ResponseEntity.ok(sessionService.getSession());
} }
@PostMapping("/music/add/{userName}/{musicId}") @PostMapping("/music/add/{musicId}")
public ResponseEntity<User> addMusic(@PathVariable String userName, @PathVariable String musicId){ public ResponseEntity<User> addMusic(@RequestHeader("Authorization") String userName, @PathVariable String musicId){
return ResponseEntity.ok(sessionService.addMusicToQueue(userName,musicId)); return ResponseEntity.ok(sessionService.addMusicToQueue(userName,musicId));
} }
@@ -30,4 +31,11 @@ public class SessionController {
return ResponseEntity.ok(sessionService.nextUser()); return ResponseEntity.ok(sessionService.nextUser());
} }
@GetMapping("testing/play/next")
public ResponseEntity<Session> playNext(){
return ResponseEntity.ok(sessionService.playNext());
}
} }
@@ -1,6 +1,7 @@
package zv.musix.controller; package zv.musix.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import zv.musix.dto.user.CreateUserRequest; import zv.musix.dto.user.CreateUserRequest;
@@ -10,7 +11,7 @@ import zv.musix.service.SessionService;
import java.util.List; import java.util.List;
@RestController @RestController
@RequestMapping("/api/user") @RequestMapping(value = "/api/user", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController { public class UserController {
@Autowired @Autowired
@@ -1,6 +1,7 @@
package zv.musix.controller; package zv.musix.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@@ -14,7 +15,7 @@ import zv.musix.service.YtService;
import java.util.List; import java.util.List;
@RestController @RestController
@RequestMapping("/api/youtube") @RequestMapping(value = "/api/youtube", produces = MediaType.APPLICATION_JSON_VALUE)
public class YoutubeController { public class YoutubeController {
@Autowired @Autowired
@@ -0,0 +1,16 @@
package zv.musix.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import zv.musix.python.json.MusicRecord;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SessionQueueElement {
String userName;
MusicRecord musicRecord;
}
@@ -1,9 +1,19 @@
package zv.musix.dto.user; package zv.musix.dto.user;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor
@NoArgsConstructor
public class CreateUserRequest { public class CreateUserRequest {
String userName; String userId;
String nickName;
public CreateUserRequest(String userId){
this.userId = userId;
}
} }
@@ -1,15 +1,11 @@
package zv.musix.entity; package zv.musix.entity;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash; import org.springframework.data.redis.core.RedisHash;
import zv.musix.dto.SessionQueueElement;
import zv.musix.python.json.MusicRecord; import zv.musix.python.json.MusicRecord;
import zv.musix.repository.SessionRepository;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -20,11 +16,11 @@ public class Session {
@Id @Id
private String sessionId = "SESSION"; private String sessionId = "SESSION";
private Integer currentUserIndex;
private MusicRecord currentMusicRecord;
private Integer userIndex; private List<SessionQueueElement> sessionQueue = new ArrayList<>();
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private List<User> users = new ArrayList<>(); private List<User> users = new ArrayList<>();
@JsonIgnore @JsonIgnore
@@ -33,45 +29,20 @@ public class Session {
throw new IllegalStateException("No users in session"); throw new IllegalStateException("No users in session");
} }
if (userIndex == null) { if (currentUserIndex == null) {
return 0; return 0;
} }
userIndex = (userIndex + 1) % users.size(); currentUserIndex = (currentUserIndex + 1) % users.size();
return userIndex; return currentUserIndex;
} }
@JsonIgnore
public List<User> addUser(User user) {
//users.stream().filter(e -> e.getUsername().equalsIgnoreCase(user.getUsername())).findAny().orElseGet(()->{users.add(user); return user;});
boolean alreadyInList = false;
for (User u : users) {
if (u.getUsername().equalsIgnoreCase(user.getUsername())) {
alreadyInList = true;
break;
}
}
if (!alreadyInList) {
users.add(user);
}
return new ArrayList<>(users);
}
public List<User> getUsers() {
return new ArrayList<>(users);
}
public User getUserByName(String userName){ public User getUserByName(String userName){
return users.stream().filter(u -> u.getUsername().equalsIgnoreCase(userName)).findFirst().orElseThrow(()->new IllegalArgumentException("userName not found in session")); return users.stream().filter(u -> u.getUserId().equalsIgnoreCase(userName)).findFirst().orElseThrow(()->new IllegalArgumentException("userName not found in session"));
} }
public User addMusicToQueue(String userName, MusicRecord musicRecord){
User user = getUserByName(userName);
user.addToMusicQueue(musicRecord);
return user;
}
} }
+13 -5
View File
@@ -1,24 +1,22 @@
package zv.musix.entity; package zv.musix.entity;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.experimental.Delegate;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash; import org.springframework.data.redis.core.RedisHash;
import zv.musix.python.json.MusicRecord; import zv.musix.python.json.MusicRecord;
import zv.musix.repository.MusicRepository;
@RedisHash("User") @RedisHash("User")
@Data @Data
public class User { public class User {
@Id @Id
private String username; private String userId;
private String nickName;
List<MusicRecord> popped = new ArrayList<>();
List<MusicRecord> musicQueue = new ArrayList<>(); List<MusicRecord> musicQueue = new ArrayList<>();
@JsonIgnore @JsonIgnore
@@ -27,6 +25,16 @@ public class User {
return new ArrayList<>(musicQueue); return new ArrayList<>(musicQueue);
} }
@JsonIgnore
public MusicRecord popMusic(){
MusicRecord musicRecord = musicQueue.getFirst();
popped.add(musicRecord);
musicQueue.removeFirst();
return musicRecord;
}
} }
@@ -18,10 +18,11 @@ public class MusicRecord {
@Id @Id
String videoId; String videoId;
String duration_seconds; String duration_seconds;
String duration;
String year; String year;
String views; String views;
Album album; Album album;
List<Artist> artistList; List<Artist> artists;
List<Thumbnails> thumbnails; List<Thumbnails> thumbnails;
@TimeToLive @TimeToLive
@@ -11,4 +11,9 @@ import java.util.Optional;
public interface MusicRepository extends CrudRepository<MusicRecord, String> { public interface MusicRepository extends CrudRepository<MusicRecord, String> {
Optional<MusicRecord> findByVideoId(String videoId); Optional<MusicRecord> findByVideoId(String videoId);
default Optional<MusicRecord> findById(String id) {
throw new UnsupportedOperationException("This method is not supported in ChildInterface.");
}
} }
@@ -1,8 +1,9 @@
package zv.musix.service; package zv.musix.service;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired; import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import zv.musix.dto.SessionQueueElement;
import zv.musix.dto.user.CreateUserRequest; import zv.musix.dto.user.CreateUserRequest;
import zv.musix.entity.Session; import zv.musix.entity.Session;
import zv.musix.entity.User; import zv.musix.entity.User;
@@ -10,16 +11,16 @@ import zv.musix.python.json.MusicRecord;
import zv.musix.repository.MusicRepository; import zv.musix.repository.MusicRepository;
import zv.musix.repository.SessionRepository; import zv.musix.repository.SessionRepository;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Service @Service
@AllArgsConstructor
public class SessionService { public class SessionService {
@Autowired
private SessionRepository sessionRepository;
@Autowired private final SessionRepository sessionRepository;
private MusicRepository musicRepository; private final MusicRepository musicRepository;
public final static String SESSION_ID = "SESSION"; public final static String SESSION_ID = "SESSION";
@@ -33,30 +34,118 @@ public class SessionService {
} }
public List<User> addUser(CreateUserRequest userRequest){ public List<User> addUser(CreateUserRequest userRequest){
User user = new User(); User newUser = new User();
user.setUsername(userRequest.getUserName()); newUser.setUserId(userRequest.getUserId());
newUser.setNickName(userRequest.getNickName());
Session session = getSession(); Session session = getSession();
session.addUser(user); boolean alreadyInList = false;
session = sessionRepository.save(session); List<User> users = session.getUsers();
for (User u : users) {
if (u.getUserId().equalsIgnoreCase(newUser.getUserId())) {
alreadyInList = true;
break;
}
}
if (!alreadyInList) {
users.add(newUser);
session = sessionRepository.save(session);
}
return session.getUsers(); return session.getUsers();
} }
public Session nextUser(){ public Session nextUser(){
Session session = getSession(); Session session = getSession();
session.setUserIndex(session.getNextUserIndex()); session.setCurrentUserIndex(getNextUserIndex(session));
User u = session.getUsers().get(session.getUserIndex()); User u = session.getUsers().get(session.getCurrentUserIndex());
session = sessionRepository.save(session); session = sessionRepository.save(session);
return session; return session;
} }
private Integer getNextUserIndex(Session session) {
List<User> users = session.getUsers();
if (users.isEmpty()) {
throw new IllegalStateException("No users in session");
}
return getNextUserIndex(session.getCurrentUserIndex(), session.getUsers().size());
}
private Integer getNextUserIndex(Integer currentUserIndex, int size){
if (currentUserIndex == null) {
return 0;
}
return (currentUserIndex + 1) % size;
}
public User addMusicToQueue(String userId, String musicId){ public User addMusicToQueue(String userId, String musicId){
MusicRecord music = musicRepository.findByVideoId(musicId).orElseThrow(() -> new IllegalArgumentException("Music not found in database")); MusicRecord music = musicRepository.findByVideoId(musicId).orElseThrow(() -> new IllegalArgumentException("Music not found in database: "+ musicId));
Session session = getSession(); Session session = getSession();
User user = session.addMusicToQueue(userId, music); User user = session.getUserByName(userId);
user.getMusicQueue().add(music);
sessionRepository.save(session); sessionRepository.save(session);
session.setSessionQueue(generateSessionQueue(getSession()));
session = sessionRepository.save(session);
return user; return user;
} }
public List<SessionQueueElement> generateSessionQueue(Session session){
List<User> newUsers = new ArrayList<>();
newUsers.addAll(session.getUsers());
session.setSessionQueue(new ArrayList<>());
Integer currentUserIndex = session.getCurrentUserIndex();
while(!newUsers.isEmpty()){
currentUserIndex = getNextUserIndex(currentUserIndex, newUsers.size());
User user = newUsers.get(currentUserIndex);
List<MusicRecord> musicQueue = user.getMusicQueue();
if(!musicQueue.isEmpty()){
MusicRecord musicRecord = user.popMusic();
session.getSessionQueue().add(new SessionQueueElement(user.getUserId(),musicRecord));
} else {
newUsers.remove(user);
}
}
return session.getSessionQueue();
}
public Session playNext() {
/* Get next user index
Megnezzuk hogy ennek a usernek van e music a queueban
Ha van akkor megvan a next user
Ha nincs akkor addig keressuk a kovetkezo usert amig nem talalunk valakit akinek van music a queueban.
Ha korbe ertunk akkor errort dobunk mondvan nincs senkinek zeneje.
Onnen tudjuk hogy korbe ertunk hogy az elejen lementjuk az elso next usernek az indexjet, es mindent "nextnel" megnezzuk hogy egyenlo e a lementett indexel
ha igen akkor error
Ha meg van a next userunk. akkor pop-oljuk a queue jat, beallitjuk az indexet a sessionben
beallitjuk a current songot
e
*/
Session session = getSession();
MusicRecord nextMusic = null;
Integer prevIndex = session.getCurrentUserIndex();
session = nextUser();
Integer currentIndex = session.getCurrentUserIndex();
while(currentIndex != prevIndex){
User currentUser = session.getUsers().get(currentIndex);
if(currentUser.getMusicQueue().isEmpty()){
session = nextUser();
currentIndex = session.getCurrentUserIndex();
}
}
nextMusic = session.getUsers().get(currentIndex).popMusic();
session = sessionRepository.save(session);
return session;
}
} }
@@ -2,8 +2,12 @@ spring.application.name=musix
spring.data.redis.host=localhost spring.data.redis.host=localhost
spring.data.redis.port=6379 spring.data.redis.port=6379
springdoc.api-docs.path=/openapi
springdoc.swagger-ui.path=/docs
logging.level.zv.musix=TRACE logging.level.zv.musix=TRACE
python.binary=/home/zoli/musix/python/venv/bin/python python.binary=/home/zoli/musix/musix/python/venv/bin/python
python.query.py=/home/zoli/musix/python/query.py python.query.py=/home/zoli/musix/musix/python/query.py
spring.main.allow-circular-references=true spring.main.allow-circular-references=true
@@ -0,0 +1,44 @@
package zv.musix;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
import static com.github.karsaig.approvalcrest.jupiter.MatcherAssert.assertThat;
import static com.github.karsaig.approvalcrest.jupiter.matcher.Matchers.sameJsonAsApproved;
@SpringBootTest
@Testcontainers
public class RedisBaseTest {
private TestInfo testInfo;
@Container
static GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:7.0-alpine"))
.withExposedPorts(6379);
// Dynamically update Spring Boot's Redis connection properties
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.redis.host", redis::getHost);
registry.add("spring.data.redis.port", () -> redis.getMappedPort(6379));
}
@BeforeEach
void setUp(TestInfo testInfo) { // 2. JUnit 5 injects it here before every test
this.testInfo = testInfo;
}
public void validateJson(Object object, String fileName){
assertThat(object, sameJsonAsApproved(testInfo).withUniqueId(fileName));
}
}
@@ -0,0 +1,195 @@
/*zv.musix.service.SessionServiceTest.sessionNextSongTest*/
[
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_1",
"videoId": "id_0_1",
"views": null,
"year": null
},
"userName": "usr_1111"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_1_1",
"videoId": "id_1_1",
"views": null,
"year": null
},
"userName": "usr_2222"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_2_1",
"videoId": "id_2_1",
"views": null,
"year": null
},
"userName": "usr_3333"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_3_1",
"videoId": "id_3_1",
"views": null,
"year": null
},
"userName": "usr_4444"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_2",
"videoId": "id_0_2",
"views": null,
"year": null
},
"userName": "usr_1111"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_1_2",
"videoId": "id_1_2",
"views": null,
"year": null
},
"userName": "usr_2222"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_2_2",
"videoId": "id_2_2",
"views": null,
"year": null
},
"userName": "usr_3333"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_3_2",
"videoId": "id_3_2",
"views": null,
"year": null
},
"userName": "usr_4444"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_3",
"videoId": "id_0_3",
"views": null,
"year": null
},
"userName": "usr_1111"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_1_3",
"videoId": "id_1_3",
"views": null,
"year": null
},
"userName": "usr_2222"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_2_3",
"videoId": "id_2_3",
"views": null,
"year": null
},
"userName": "usr_3333"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_3_3",
"videoId": "id_3_3",
"views": null,
"year": null
},
"userName": "usr_4444"
}
]
@@ -0,0 +1,187 @@
/*zv.musix.service.SessionServiceTest.sessionNextSongTest*/
[
{
"musicQueue": [
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_1",
"videoId": "id_0_1",
"views": null,
"year": null
},
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_2",
"videoId": "id_0_2",
"views": null,
"year": null
},
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_3",
"videoId": "id_0_3",
"views": null,
"year": null
}
],
"nickName": null,
"popped": [],
"userId": "usr_1111"
},
{
"musicQueue": [
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_1_1",
"videoId": "id_1_1",
"views": null,
"year": null
},
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_1_2",
"videoId": "id_1_2",
"views": null,
"year": null
},
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_1_3",
"videoId": "id_1_3",
"views": null,
"year": null
}
],
"nickName": null,
"popped": [],
"userId": "usr_2222"
},
{
"musicQueue": [
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_2_1",
"videoId": "id_2_1",
"views": null,
"year": null
},
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_2_2",
"videoId": "id_2_2",
"views": null,
"year": null
},
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_2_3",
"videoId": "id_2_3",
"views": null,
"year": null
}
],
"nickName": null,
"popped": [],
"userId": "usr_3333"
},
{
"musicQueue": [
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_3_1",
"videoId": "id_3_1",
"views": null,
"year": null
},
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_3_2",
"videoId": "id_3_2",
"views": null,
"year": null
},
{
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_3_3",
"videoId": "id_3_3",
"views": null,
"year": null
}
],
"nickName": null,
"popped": [],
"userId": "usr_4444"
}
]
@@ -0,0 +1,35 @@
/*zv.musix.service.SessionServiceTest.userIsSkippedWhenNoMusicIsInHisQueue*/
[
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_1",
"videoId": "id_0_1",
"views": null,
"year": null
},
"userName": "usr_1111"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_2_1",
"videoId": "id_2_1",
"views": null,
"year": null
},
"userName": "usr_3333"
}
]
@@ -0,0 +1,2 @@
/*zv.musix.service.SessionServiceTest.sessionQueueWhenNewSongIsAddedByUsersInOrderTest*/
[]
@@ -0,0 +1,19 @@
/*zv.musix.service.SessionServiceTest.sessionQueueWhenNewSongIsAddedByUsersInOrderTest*/
[
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_1",
"videoId": "id_0_1",
"views": null,
"year": null
},
"userName": "usr_1111"
}
]
@@ -0,0 +1,35 @@
/*zv.musix.service.SessionServiceTest.sessionQueueWhenNewSongIsAddedByUsersInOrderTest*/
[
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_0_1",
"videoId": "id_0_1",
"views": null,
"year": null
},
"userName": "usr_1111"
},
{
"musicRecord": {
"album": null,
"artistList": null,
"category": null,
"duration_seconds": null,
"resultType": null,
"thumbnails": null,
"timeoutInSeconds": 600,
"title": "title_1_1",
"videoId": "id_1_1",
"views": null,
"year": null
},
"userName": "usr_2222"
}
]
@@ -0,0 +1,114 @@
package zv.musix.service;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import zv.musix.RedisBaseTest;
import zv.musix.dto.user.CreateUserRequest;
import zv.musix.entity.Session;
import zv.musix.python.json.MusicRecord;
import zv.musix.repository.MusicRepository;
import zv.musix.repository.SessionRepository;
import java.util.Optional;
import static org.mockito.Mockito.when;
import static com.github.karsaig.approvalcrest.jupiter.MatcherAssert.assertThat;
import static com.github.karsaig.approvalcrest.jupiter.matcher.Matchers.sameJsonAsApproved;
public class SessionServiceTest extends RedisBaseTest {
@MockitoBean
private MusicRepository musicRepository;
@Autowired
SessionService sessionService;
@Autowired
SessionRepository sessionRepository;
@AfterEach
public void reset(){
sessionRepository.deleteAll();
sessionRepository.findById(SessionService.SESSION_ID).orElseGet(() -> sessionRepository.save(new Session()));
}
@BeforeEach
public void prepareSongMocks(){
for(int j = 0; j < 4; j++){
for(int i = 0; i < 4; i++){
MusicRecord musicRecord = new MusicRecord();
musicRecord.setVideoId("id_"+i+"_"+j);
musicRecord.setTitle("title_"+i+"_"+j);
when(musicRepository.findByVideoId("id_"+i+"_"+j)).thenReturn(Optional.of(musicRecord));
}
}
}
@Test
void sessionNextSongTest() {
Session session = prepareSession();
validateJson(sessionService.generateSessionQueue(session),"session_queue");
}
@Test
void sessionQueueWhenNewSongIsAddedByUsersInOrderTest(){
Session session = sessionService.getSession();
sessionService.addUser( new CreateUserRequest("usr_1111"));
sessionService.addUser( new CreateUserRequest("usr_2222"));
validateJson(sessionService.getSession().getSessionQueue(),"session_queue");
sessionService.addMusicToQueue("usr_1111","id_0_1");
validateJson(sessionService.getSession().getSessionQueue(),"session_queue_1");
sessionService.addMusicToQueue("usr_2222","id_1_1");
validateJson(sessionService.getSession().getSessionQueue(),"session_queue_2");
}
@Test
void userIsSkippedWhenNoMusicIsInHisQueue(){
Session session = sessionService.getSession();
sessionService.addUser( new CreateUserRequest("usr_1111"));
sessionService.addUser( new CreateUserRequest("usr_2222"));
sessionService.addUser( new CreateUserRequest("usr_3333"));
sessionService.addMusicToQueue("usr_1111","id_0_1");
sessionService.addMusicToQueue("usr_3333","id_2_1");
validateJson(sessionService.getSession().getSessionQueue(),"session_queue_not_in_order");
}
private Session prepareSession() {
Session session = sessionService.getSession();
sessionService.addUser( new CreateUserRequest("usr_1111"));
sessionService.addUser( new CreateUserRequest("usr_2222"));
sessionService.addUser( new CreateUserRequest("usr_3333"));
sessionService.addUser( new CreateUserRequest("usr_4444"));
sessionService.addMusicToQueue("usr_1111","id_0_1");
sessionService.addMusicToQueue("usr_1111","id_0_2");
sessionService.addMusicToQueue("usr_1111","id_0_3");
sessionService.addMusicToQueue("usr_2222","id_1_1");
sessionService.addMusicToQueue("usr_2222","id_1_2");
sessionService.addMusicToQueue("usr_2222","id_1_3");
sessionService.addMusicToQueue("usr_3333","id_2_1");
sessionService.addMusicToQueue("usr_3333","id_2_2");
sessionService.addMusicToQueue("usr_3333","id_2_3");
sessionService.addMusicToQueue("usr_4444","id_3_1");
sessionService.addMusicToQueue("usr_4444","id_3_2");
sessionService.addMusicToQueue("usr_4444","id_3_3");
validateJson(sessionService.getSession().getUsers(),"starting_user_state");
return sessionService.getSession();
}
}
-22
View File
@@ -1,22 +0,0 @@
from ytmusicapi import YTMusic
import json
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--query", type=str, required=True)
parser.add_argument("--filter", type=str, required=False)
parser.add_argument("--limit", type=int, required=False)
args = parser.parse_args()
limit=20;
filter="songs"
if(args.limit != None):
limit=args.limit;
if(args.filter != None):
filter=args.filter
ytmusic = YTMusic()
search_results = ytmusic.search(args.query, filter=filter, limit = limit)
print(search_results)