2 Commits
Author SHA1 Message Date
janishutz 94906c3499 chore: try to get types for musickit-js 2026-08-23 11:57:59 +02:00
janishutz c38b8d3115 feat: dev token loading, basic setup for musickit 2026-08-23 09:28:19 +02:00
7 changed files with 175 additions and 37 deletions
+71 -29
View File
@@ -1,15 +1,15 @@
import vue from 'eslint-plugin-vue';
import eslint from '@eslint/js'; import eslint from '@eslint/js';
import globals from 'globals'; import globals from 'globals';
import typescript from '@typescript-eslint/eslint-plugin';
import stylistic from '@stylistic/eslint-plugin'; import stylistic from '@stylistic/eslint-plugin';
import tseslint from 'typescript-eslint'; import tseslint from 'typescript-eslint';
import typescript from '@typescript-eslint/eslint-plugin';
import vue from 'eslint-plugin-vue';
const style = { const style = {
'plugins': { 'plugins': {
'@stylistic': stylistic, '@stylistic': stylistic,
'@stylistic/js': stylistic, '@stylistic/js': stylistic,
'@stylistic/ts': stylistic, '@stylistic/ts': stylistic
}, },
'files': [ 'files': [
'**/*.ts', '**/*.ts',
@@ -20,11 +20,26 @@ const style = {
'**/*.jsx' '**/*.jsx'
], ],
'rules': { 'rules': {
'sort-imports': [
'warn',
{
'ignoreCase': false,
'ignoreDeclarationSort': false,
'ignoreMemberSort': false,
'memberSyntaxSortOrder': [
'none',
'all',
'multiple',
'single'
],
'allowSeparatedGroups': false
}
],
// Formatting // Formatting
'@stylistic/array-bracket-newline': [ '@stylistic/array-bracket-newline': [
'error', 'error',
{ {
'multiline': true, 'multiline': false,
'minItems': 2 'minItems': 2
} }
], ],
@@ -35,7 +50,8 @@ const style = {
'@stylistic/array-element-newline': [ '@stylistic/array-element-newline': [
'error', 'error',
{ {
'multiline': true, 'consistent': false,
'multiline': false,
'minItems': 2 'minItems': 2
} }
], ],
@@ -56,7 +72,14 @@ const style = {
], ],
'@stylistic/brace-style': [ '@stylistic/brace-style': [
'error', 'error',
'1tbs' '1tbs',
{
'allowSingleLine': false
}
],
'@stylistic/comma-dangle': [
'error',
'never'
], ],
'@stylistic/comma-spacing': [ '@stylistic/comma-spacing': [
'error', 'error',
@@ -73,9 +96,9 @@ const style = {
'error', 'error',
'property' 'property'
], ],
'@stylistic/eol-last': [ '@stylistic/function-call-argument-newline': [
'error', 'error',
'always' 'consistent'
], ],
'@stylistic/function-call-spacing': [ '@stylistic/function-call-spacing': [
'error', 'error',
@@ -83,11 +106,7 @@ const style = {
], ],
'@stylistic/function-paren-newline': [ '@stylistic/function-paren-newline': [
'error', 'error',
'multiline' 'multiline-arguments'
],
'@stylistic/function-call-argument-newline': [
'error',
'consistent'
], ],
'@stylistic/implicit-arrow-linebreak': [ '@stylistic/implicit-arrow-linebreak': [
'error', 'error',
@@ -97,6 +116,10 @@ const style = {
'error', 'error',
4 4
], ],
'@stylistic/indent-binary-ops': [
'error',
4
],
'@stylistic/key-spacing': [ '@stylistic/key-spacing': [
'error', 'error',
{ {
@@ -118,18 +141,31 @@ const style = {
'@stylistic/max-len': [ '@stylistic/max-len': [
'warn', 'warn',
{ {
'code': 90, 'code': 140,
'comments': 100, 'comments': 160,
'ignoreComments': false, 'ignoreComments': false,
'ignoreUrls': true, 'ignoreUrls': true,
'ignoreStrings': false 'ignoreStrings': true,
'ignoreTemplateLiterals': true,
'ignoreRegExpLiterals': true
} }
], ],
'@stylistic/max-statements-per-line': [
'error',
{
'max': 1
}
],
'@stylistic/multiline-ternary': [
'error',
'always-multiline'
],
'@stylistic/new-parens': [ '@stylistic/new-parens': [
'error', 'error',
'always' 'always'
], ],
'@stylistic/newline-per-chained-call': [ 'error' ], '@stylistic/newline-per-chained-call': 'error',
'@stylistic/no-confusing-arrow': 'error',
'@stylistic/no-extra-parens': [ '@stylistic/no-extra-parens': [
'error', 'error',
'all', 'all',
@@ -167,17 +203,17 @@ const style = {
'always' 'always'
], ],
'@stylistic/object-property-newline': 'error', '@stylistic/object-property-newline': 'error',
'@stylistic/one-var-declaration-per-line': 'error',
'@stylistic/operator-linebreak': [ '@stylistic/operator-linebreak': [
'error', 'error',
'before' 'before'
], ],
'@stylistic/one-var-declaration-per-line': 'error',
'@stylistic/padded-blocks': [ '@stylistic/padded-blocks': [
'error', 'error',
{ {
'blocks': 'never', 'blocks': 'never',
'classes': 'always', 'classes': 'always',
'switches': 'never', 'switches': 'never'
} }
], ],
@@ -561,7 +597,7 @@ const style = {
'blankLine': 'always', 'blankLine': 'always',
'prev': 'class', 'prev': 'class',
'next': '*' 'next': '*'
}, }
], ],
'@stylistic/quote-props': [ '@stylistic/quote-props': [
'error', 'error',
@@ -613,17 +649,23 @@ const style = {
'error', 'error',
'always' 'always'
], ],
'@stylistic/switch-colon-spacing': 'error',
'@stylistic/template-curly-spacing': [ '@stylistic/template-curly-spacing': [
'error', 'error',
'always' 'always'
], ],
'@stylistic/switch-colon-spacing': 'error', '@stylistic/template-tag-spacing': [
'error',
'always'
],
'@stylistic/type-generic-spacing': 'error',
'@stylistic/type-named-tuple-spacing': 'error',
'@stylistic/wrap-iife': [ '@stylistic/wrap-iife': [
'error', 'error',
'inside' 'inside'
], ],
'@stylistic/wrap-regex': 'error', '@stylistic/wrap-regex': 'error',
'@stylistic/ts/type-annotation-spacing': 'error', '@stylistic/ts/type-annotation-spacing': 'error'
} }
}; };
@@ -642,15 +684,15 @@ export default tseslint.config(
'ecmaVersion': 'latest', 'ecmaVersion': 'latest',
'globals': globals.browser, 'globals': globals.browser,
'parserOptions': { 'parserOptions': {
'parser': tseslint.parser, 'parser': tseslint.parser
}, }
}, },
'plugins': { 'plugins': {
'vue': vue, 'vue': vue,
'@stylistic': stylistic, '@stylistic': stylistic,
'@stylistic/js': stylistic, '@stylistic/js': stylistic,
'@stylistic/ts': stylistic, '@stylistic/ts': stylistic,
'@typescript-eslint': typescript, '@typescript-eslint': typescript
}, },
'extends': [ 'extends': [
eslint.configs.recommended, eslint.configs.recommended,
@@ -694,9 +736,9 @@ export default tseslint.config(
'error', 'error',
{ {
'singleline': 3, 'singleline': 3,
'multiline': 1, 'multiline': 1
}
]
}
} }
],
},
},
); );
+19
View File
@@ -0,0 +1,19 @@
{
"dependencies": {
"express": "^5.2.1",
"express-session": "^1.19.0",
"jsonwebtoken": "^9.0.3"
},
"devDependencies": {
"eslint": "^10.9.0",
"@types/express": "^5.0.6",
"@types/express-session": "^1.19.0",
"@types/node": "^26.2.0",
"@eslint/js": "^10.0.1",
"@stylistic/eslint-plugin": "^5.10.0",
"@types/jsonwebtoken": "^9.0.10",
"eslint-plugin-vue": "^10.10.0",
"globals": "^17.11.0",
"typescript-eslint": "^8.67.0"
}
}
+54
View File
@@ -0,0 +1,54 @@
import express from 'express';
import expressSession from 'express-session';
import fs from 'fs';
import jwt from 'jsonwebtoken';
import path from 'path';
const app = express();
app.use( expressSession( {
'secret': '',
'resave': true,
'saveUninitialized': false
} ) );
app.get( '/', ( request: express.Request, response: express.Response ) => {
response.send( 'HELLO WORLD' );
} );
const privateKey = fs.readFileSync( path.join(
__dirname,
'/config/apple_private_key.p8'
) ).toString();
const musicApiConfig = JSON.parse( fs.readFileSync( path.join(
__dirname,
'/config/apple-music-api.config.secret.json'
) ).toString() );
app.get( '/dev-token', ( request: express.Request, response: express.Response ) => {
// sign dev token
const now = new Date().getTime();
const tomorrow = now + ( 24 * 3600 * 1000 );
const jwtToken = jwt.sign( {
'iss': musicApiConfig.teamID,
'iat': Math.floor( now / 1000 ),
'exp': Math.floor( tomorrow / 1000 )
}, privateKey, {
'algorithm': 'ES256',
'keyid': musicApiConfig.keyID
} );
response.send( jwtToken );
} );
app.use( ( _request: express.Request, response: express.Response ) => {
response.sendFile( path.join( __dirname, '' ) );
} );
const PORT = process.env.PORT || 8080;
app.listen( PORT );
+1
View File
@@ -0,0 +1 @@
VITE_BACKEND_URL=http://localhost:8080
+2
View File
@@ -19,12 +19,14 @@
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@stylistic/eslint-plugin": "^5.10.0", "@stylistic/eslint-plugin": "^5.10.0",
"@tsconfig/node24": "^24.0.4", "@tsconfig/node24": "^24.0.4",
"@types/musickit-js": "^1.0.11",
"@types/node": "^24.13.3", "@types/node": "^24.13.3",
"@vitejs/plugin-vue": "^6.0.8", "@vitejs/plugin-vue": "^6.0.8",
"@vue/tsconfig": "^0.9.1", "@vue/tsconfig": "^0.9.1",
"eslint-plugin-vue": "^10.10.0", "eslint-plugin-vue": "^10.10.0",
"globals": "^17.11.0", "globals": "^17.11.0",
"npm-run-all2": "^9.0.2", "npm-run-all2": "^9.0.2",
"sass-embedded": "^1.103.1",
"typescript": "~6.0.0", "typescript": "~6.0.0",
"typescript-eslint": "^8.67.0", "typescript-eslint": "^8.67.0",
"vite": "^8.1.5", "vite": "^8.1.5",
+20 -2
View File
@@ -1,5 +1,23 @@
export const useMusicKit = () => { export const useMusicKit = ( storefront: string = 'ch' ) => {
const init = () => { const init = async () => {
const res = await fetch( import.meta.env.VITE_BACKEND_URL + '/', {
'credentials': 'include'
} );
if ( res.status === 200 ) {
const token = await res.text();
await window.MusicKit.configure( {
'developerToken': token,
'app': {
'name': 'MusicPlayer',
'build': '4',
'icon': 'https://music.janishutz.com/logo.jpg'
},
'storefront': storefront.toUpperCase()
} );
}
isInit = true; isInit = true;
}; };
+8 -6
View File
@@ -2,22 +2,24 @@
<div class="not-found-view"> <div class="not-found-view">
<h1>404</h1> <h1>404</h1>
<p>Page not found</p> <p>Page not found</p>
<RouterLink to="/" class="button primary"> <RouterLink to="/" class="fancy-button">
Home Home
</RouterLink> </RouterLink>
</div> </div>
</template> </template>
<script setup lang="ts"> <style scoped lang="scss">
</script>
<style scoped>
.not-found-view { .not-found-view {
height: 100vh; height: 100vh;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
h1 {
font-size: 20vh;
line-height: 100%;
margin: 10px;
}
} }
</style> </style>