diff --git a/MusicPlayerV2-GUI/index.html b/MusicPlayerV2-GUI/index.html index 4005727..3f895d1 100644 --- a/MusicPlayerV2-GUI/index.html +++ b/MusicPlayerV2-GUI/index.html @@ -8,6 +8,8 @@ + + MusicPlayer diff --git a/MusicPlayerV2-GUI/package-lock.json b/MusicPlayerV2-GUI/package-lock.json index 9900e3a..0d467ec 100644 --- a/MusicPlayerV2-GUI/package-lock.json +++ b/MusicPlayerV2-GUI/package-lock.json @@ -13,6 +13,7 @@ "@jridgewell/sourcemap-codec": "^1.4.15", "@rollup/plugin-inject": "^5.0.5", "buffer": "^6.0.3", + "colorthief": "^2.2.0", "music-metadata-browser": "^2.5.10", "musickit-typescript": "^1.2.4", "pinia": "^2.1.7", @@ -1585,6 +1586,15 @@ "dev": true, "license": "MIT" }, + "node_modules/colorthief": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/colorthief/-/colorthief-2.2.0.tgz", + "integrity": "sha512-jtS2rrSQQMHqE4tkzpZu7l4MDOwKuZTxzsHdhFmxwshCX/5snPIzC+CwwHOwaACa6bwYV3qHwvnCSLqKvsC4Dw==", + "license": "MIT", + "engines": { + "node": ">=10.15.3" + } + }, "node_modules/computeds": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", diff --git a/MusicPlayerV2-GUI/package.json b/MusicPlayerV2-GUI/package.json index c230f0b..cc75698 100644 --- a/MusicPlayerV2-GUI/package.json +++ b/MusicPlayerV2-GUI/package.json @@ -17,6 +17,7 @@ "@jridgewell/sourcemap-codec": "^1.4.15", "@rollup/plugin-inject": "^5.0.5", "buffer": "^6.0.3", + "colorthief": "^2.2.0", "music-metadata-browser": "^2.5.10", "musickit-typescript": "^1.2.4", "pinia": "^2.1.7", diff --git a/MusicPlayerV2-GUI/src/components/playerView.vue b/MusicPlayerV2-GUI/src/components/playerView.vue index dde102a..77aa5e4 100644 --- a/MusicPlayerV2-GUI/src/components/playerView.vue +++ b/MusicPlayerV2-GUI/src/components/playerView.vue @@ -29,13 +29,15 @@

{{ nicePlaybackPos }}

-

{{ niceDuration }}

+

{{ niceDuration }}

repeat{{ repeatMode }} + share + close shuffle{{ shuffleMode }}
@@ -82,11 +84,13 @@ const nicePlaybackPos = ref( '00:00' ); const niceDuration = ref( '00:00' ); const isShowingRemainingTime = ref( false ); + let isShowingRemainingTimeBackend = false; const currentlyPlayingSongArtist = ref( '' ); const pos = ref( 0 ); const duration = ref( 0 ); const notifications = ref( notificationsModule ); const notificationHandler = new NotificationHandler(); + const isConnectedToNotifier = ref( false ); const emits = defineEmits( [ 'playerStateChange' ] ); @@ -163,6 +167,9 @@ getDetails(); startProgressTracker(); }, 2000 ); + } else if ( action === 'start-share' ) { + // TODO: Open popup, then send data with popup returns + notificationHandler.connect( 'test' ); } } @@ -170,9 +177,15 @@ const controlUI = ( action: string ) => { if ( action === 'show' ) { isShowingFullScreenPlayer.value = true; + isShowingRemainingTime.value = isShowingRemainingTimeBackend; emits( 'playerStateChange', 'show' ); } else if ( action === 'hide' ) { isShowingFullScreenPlayer.value = false; + isShowingRemainingTimeBackend = isShowingRemainingTime.value; + isShowingRemainingTime.value = false; + try { + prepNiceDurationTime( player.getPlayingSong() ); + } catch ( err ) { /* empty */ } emits( 'playerStateChange', 'hide' ); } } @@ -300,18 +313,7 @@ hasReachedEnd = false; isPlaying.value = true; const playingSong = player.getPlayingSong(); - duration.value = playingSong.duration; - const minuteCounts = Math.floor( ( playingSong.duration ) / 60 ); - niceDuration.value = String( minuteCounts ) + ':'; - if ( ( '' + minuteCounts ).length === 1 ) { - niceDuration.value = '0' + minuteCounts + ':'; - } - const secondCounts = Math.floor( ( playingSong.duration ) - minuteCounts * 60 ); - if ( ( '' + secondCounts ).length === 1 ) { - niceDuration.value += '0' + secondCounts; - } else { - niceDuration.value += secondCounts; - } + prepNiceDurationTime( playingSong ); progressTracker = setInterval( () => { pos.value = player.getPlaybackPos(); if ( pos.value > playingSong.duration - 1 && !hasReachedEnd ) { @@ -319,6 +321,9 @@ hasReachedEnd = true; if ( repeatMode.value === '_one_on' ) { player.goToPos( 0 ); + setTimeout( () => { + control( 'play' ); + }, 500 ); } else { control( 'next' ); } @@ -356,6 +361,21 @@ }, 50 ); } + const prepNiceDurationTime = ( playingSong: Song ) => { + duration.value = playingSong.duration; + const minuteCounts = Math.floor( ( playingSong.duration ) / 60 ); + niceDuration.value = String( minuteCounts ) + ':'; + if ( ( '' + minuteCounts ).length === 1 ) { + niceDuration.value = '0' + minuteCounts + ':'; + } + const secondCounts = Math.floor( ( playingSong.duration ) - minuteCounts * 60 ); + if ( ( '' + secondCounts ).length === 1 ) { + niceDuration.value += '0' + secondCounts; + } else { + niceDuration.value += secondCounts; + } + } + const stopProgressTracker = () => { try { clearInterval( progressTracker ); diff --git a/MusicPlayerV2-GUI/src/components/playlistsView.vue b/MusicPlayerV2-GUI/src/components/playlistsView.vue index 46fa4be..020fe87 100644 --- a/MusicPlayerV2-GUI/src/components/playlistsView.vue +++ b/MusicPlayerV2-GUI/src/components/playlistsView.vue @@ -5,10 +5,11 @@ Loading... -
-

You are not logged into Apple Music.

+
+

You are not logged into Apple Music. We therefore can't show you your playlists. Change that

+

Use the below button to load songs from your local disk


- +

Please select at least one song to proceed!

@@ -87,5 +88,19 @@ .pl-loader-button { background-color: white; + border: none; + padding: 10px; + border-radius: 5px; + margin: 5px; + font-size: 1rem; + cursor: pointer; + } + + #load-button { + font-size: 1.5rem; + } + + .not-logged-in { + width: 80%; } \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/components/popupModule.vue b/MusicPlayerV2-GUI/src/components/popupModule.vue index 386135a..63868e1 100644 --- a/MusicPlayerV2-GUI/src/components/popupModule.vue +++ b/MusicPlayerV2-GUI/src/components/popupModule.vue @@ -1,481 +1,298 @@ - - + + + + \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/components/searchView.vue b/MusicPlayerV2-GUI/src/components/searchView.vue index b7ff761..f7e368a 100644 --- a/MusicPlayerV2-GUI/src/components/searchView.vue +++ b/MusicPlayerV2-GUI/src/components/searchView.vue @@ -136,7 +136,7 @@ width: 100vw; left: 0; transition: all 1s; - z-index: 2; + z-index: 50; } #search-bar.search-shown { diff --git a/MusicPlayerV2-GUI/src/main.ts b/MusicPlayerV2-GUI/src/main.ts index 136a124..5e21ab7 100644 --- a/MusicPlayerV2-GUI/src/main.ts +++ b/MusicPlayerV2-GUI/src/main.ts @@ -9,6 +9,6 @@ const app = createApp(App) app.use(createPinia()) app.use(router) -localStorage.setItem( 'url', 'http://localhost:8081' ); +localStorage.setItem( 'url', 'http://localhost:8082' ); app.mount('#app') diff --git a/MusicPlayerV2-GUI/src/router/index.ts b/MusicPlayerV2-GUI/src/router/index.ts index 4d63763..109dfe7 100644 --- a/MusicPlayerV2-GUI/src/router/index.ts +++ b/MusicPlayerV2-GUI/src/router/index.ts @@ -1,5 +1,6 @@ import { createRouter, createWebHistory } from 'vue-router'; import HomeView from '@/views/HomeView.vue'; +import { useUserStore } from '@/stores/userStore'; const router = createRouter( { history: createWebHistory( import.meta.env.BASE_URL ), @@ -18,7 +19,7 @@ const router = createRouter( { name: 'app', component: () => import( '../views/AppView.vue' ), meta: { - 'authRequired': false, + 'authRequired': true, 'title': 'App' } }, @@ -41,30 +42,14 @@ const router = createRouter( { // next(); // } ); -// router.beforeEach( ( to ) => { -// const userStore = useUserStore(); -// const isUserAuthenticated = userStore.getUserAuthenticated; -// const isAdminAuthenticated = userStore.getAdminAuthenticated; - -// if ( to.meta.adminAuthRequired && !isAdminAuthenticated ) { -// return { name: 'adminLogin' }; -// } else if ( to.name === 'adminLogin' && isAdminAuthenticated ) { -// return { name: 'admin' }; -// } -// // else if ( isUserAuthenticated && to.name === 'login' ) { -// // return { name: 'account' }; -// // } -// else if ( !isUserAuthenticated && to.name === 'checkout' ) { -// localStorage.setItem( 'redirect', '/checkout' ); -// return { name: 'login' }; -// } else if ( !isUserAuthenticated && to.meta.authRequired ) { -// localStorage.setItem( 'redirect', to.fullPath ); -// return { name: 'login' }; -// } - -// // TODO: Make titles adapt to languages as well -// // TODO: Make multi-lang -// } ); +router.beforeEach( ( to ) => { + const userStore = useUserStore(); + const isUserAuthenticated = userStore.getUserAuthenticated; + if ( !isUserAuthenticated && to.meta.authRequired ) { + localStorage.setItem( 'redirect', to.fullPath ); + return { name: 'home' }; + } +} ); router.afterEach( ( to ) => { window.scrollTo( { top: 0, behavior: 'smooth' } ); diff --git a/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts b/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts index 25c5d20..c240103 100644 --- a/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts +++ b/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts @@ -13,11 +13,15 @@ import { io, type Socket } from "socket.io-client" class NotificationHandler { socket: Socket; + roomName: string; + roomToken: string; constructor () { this.socket = io( localStorage.getItem( 'url' ) ?? '', { autoConnect: false, } ); + this.roomName = ''; + this.roomToken = ''; } /** @@ -25,19 +29,36 @@ class NotificationHandler { * @param {string} roomName * @returns {Promise} */ - connect ( roomName: string ): Promise { - fetch( localStorage.getItem( 'url' ) + '/createRoomToken', { credentials: 'include' } ).then( res => { - if ( res.status === 200 ) { - res.json().then( json => { - - } ); - } + connect ( roomName: string ): Promise { + return new Promise( ( resolve, reject ) => { + fetch( localStorage.getItem( 'url' ) + '/createRoomToken?roomName=' + roomName, { credentials: 'include' } ).then( res => { + if ( res.status === 200 ) { + res.text().then( text => { + this.roomToken = text; + this.roomName = roomName; + this.socket.connect(); + this.socket.emit( 'create-room', { + name: this.roomName, + token: this.roomToken + }, ( res: { status: boolean, msg: string } ) => { + if ( res.status === true) { + resolve(); + } else { + reject( 'ERR_ROOM_CONNECTING' ); + } + } ); + } ); + } else if ( res.status === 409 ) { + reject( 'ERR_CONFLICT' ); + } else { + reject( 'ERR_ROOM_CREATING' ); + } + } ); } ); - } /** - * Description + * Emit an event * @param {string} event The event to emit * @param {any} data * @returns {void} @@ -46,12 +67,30 @@ class NotificationHandler { this.socket.emit( event, data ); } + /** + * Register a listener function for an event + * @param {string} event The event to listen for + * @param {( data: any ) => void} cb The callback function / listener function + * @returns {void} + */ registerListener ( event: string, cb: ( data: any ) => void ): void { this.socket.on( event, cb ); } + /** + * Disconnect from the server + * @returns {any} + */ disconnect (): void { this.socket.disconnect(); + this.socket.emit( 'create-room', { + name: this.roomName, + token: this.roomToken + }, ( res: { status: boolean, msg: string } ) => { + if ( !res.status ) { + alert( 'Unable to delete the room you were just in. The name will be blocked until the next server restart!' ); + } + } ); } } diff --git a/MusicPlayerV2-GUI/src/views/AppView.vue b/MusicPlayerV2-GUI/src/views/AppView.vue index a1d6a15..1f3d64d 100644 --- a/MusicPlayerV2-GUI/src/views/AppView.vue +++ b/MusicPlayerV2-GUI/src/views/AppView.vue @@ -18,7 +18,7 @@ import playerView from '@/components/playerView.vue'; import libraryView from '@/components/libraryView.vue'; import { ref } from 'vue'; -import type { ReadFile } from '@/scripts/song'; + import type { ReadFile } from '@/scripts/song'; const isLoggedIntoAppleMusic = ref( false ); const isReady = ref( false ); @@ -37,6 +37,7 @@ import type { ReadFile } from '@/scripts/song'; let loginChecker = 0; const logIntoAppleMusic = () => { + player.value.logIntoAppleMusic(); loginChecker = setInterval( () => { if ( player.value.getAuth()[ 0 ] ) { isLoggedIntoAppleMusic.value = true; diff --git a/MusicPlayerV2-GUI/src/views/HomeView.vue b/MusicPlayerV2-GUI/src/views/HomeView.vue index d1ada71..9ce4139 100644 --- a/MusicPlayerV2-GUI/src/views/HomeView.vue +++ b/MusicPlayerV2-GUI/src/views/HomeView.vue @@ -1,19 +1,70 @@ diff --git a/MusicPlayerV2-GUI/src/views/RemoteView.vue b/MusicPlayerV2-GUI/src/views/RemoteView.vue new file mode 100644 index 0000000..e69de29 diff --git a/MusicPlayerV2-GUI/src/views/ShowcaseView.vue b/MusicPlayerV2-GUI/src/views/ShowcaseView.vue index e69de29..c2dbbf8 100644 --- a/MusicPlayerV2-GUI/src/views/ShowcaseView.vue +++ b/MusicPlayerV2-GUI/src/views/ShowcaseView.vue @@ -0,0 +1,420 @@ + + + + \ No newline at end of file diff --git a/MusicPlayerV2-GUI/vite.config.ts b/MusicPlayerV2-GUI/vite.config.ts index 28acfe6..fee8c04 100644 --- a/MusicPlayerV2-GUI/vite.config.ts +++ b/MusicPlayerV2-GUI/vite.config.ts @@ -27,6 +27,6 @@ export default defineConfig({ }, }, server: { - port: 8080 + port: 8081 } }) diff --git a/backend/config/sdk.config.testing.json b/backend/config/sdk.config.testing.json index 9cc41f2..5844b7a 100644 --- a/backend/config/sdk.config.testing.json +++ b/backend/config/sdk.config.testing.json @@ -2,5 +2,6 @@ "token": "phafowegoväbwpb$weapvbpvfwcvfäawef39'ü0wtäqgpt5^ü62q'ẗ9wäa3g", "name": "localhost:8082", "client": "localhost:8081", - "backendURL": "http://localhost:8080" + "backendURL": "http://localhost:8080", + "failReturnURL": "http://localhost:8081" } \ No newline at end of file diff --git a/backend/src/app.ts b/backend/src/app.ts index 93e7559..dc6709b 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -67,7 +67,23 @@ const run = () => { cb( { status: true, msg: 'ADDED_TO_ROOM' - } ) + } ); + } else { + cb( { + status: false, + msg: 'ERR_TOKEN_INVALID' + } ); + } + } ); + + socket.on( 'delete-room', ( room: { name: string, token: string }, cb: ( res: { status: boolean, msg: string } ) => void ) => { + if ( room.token === socketData[ room.name ].roomToken ) { + socket.leave( room.name ); + socketData[ room.name ] = undefined; + cb( { + status: true, + msg: 'ROOM_DELETED' + } ); } else { cb( { status: false, @@ -193,7 +209,7 @@ const run = () => { } ); - const PORT = process.env.PORT || 8081; + const PORT = process.env.PORT || 8082; httpServer.listen( PORT ); }