feat: Add new dependencies and SunoApi class for audio generation
functionality Added new dependencies for axios, pino, and user-agents in the package.json and pnpm-lock.yaml files to support audio generation functionality. Additionally, implemented a new SunoApi class in the lib directory to handle the audio generation process, including functions for generating, customizing, and retrieving audio content from the Suno.ai API. This addresses the need for enhanced audio processing capabilities and lays the groundwork for efficient integration with the Suno.ai API. No issues are associated with these additions.
This commit is contained in:
		
							parent
							
								
									12f31cb3ea
								
							
						
					
					
						commit
						0fcf77df6d
					
				@ -15,14 +15,18 @@
 | 
			
		||||
    "lint": "next lint"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "axios": "^1.6.8",
 | 
			
		||||
    "next": "14.1.4",
 | 
			
		||||
    "pino": "^8.19.0",
 | 
			
		||||
    "react": "^18",
 | 
			
		||||
    "react-dom": "^18"
 | 
			
		||||
    "react-dom": "^18",
 | 
			
		||||
    "user-agents": "^1.1.156"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/node": "^20",
 | 
			
		||||
    "@types/react": "^18",
 | 
			
		||||
    "@types/react-dom": "^18",
 | 
			
		||||
    "@types/user-agents": "^1.0.4",
 | 
			
		||||
    "autoprefixer": "^10.0.1",
 | 
			
		||||
    "eslint": "^8",
 | 
			
		||||
    "eslint-config-next": "14.1.4",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										223
									
								
								pnpm-lock.yaml
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								pnpm-lock.yaml
									
									
									
									
									
								
							@ -5,15 +5,24 @@ settings:
 | 
			
		||||
  excludeLinksFromLockfile: false
 | 
			
		||||
 | 
			
		||||
dependencies:
 | 
			
		||||
  axios:
 | 
			
		||||
    specifier: ^1.6.8
 | 
			
		||||
    version: 1.6.8
 | 
			
		||||
  next:
 | 
			
		||||
    specifier: 14.1.4
 | 
			
		||||
    version: 14.1.4(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
  pino:
 | 
			
		||||
    specifier: ^8.19.0
 | 
			
		||||
    version: 8.19.0
 | 
			
		||||
  react:
 | 
			
		||||
    specifier: ^18
 | 
			
		||||
    version: 18.2.0
 | 
			
		||||
  react-dom:
 | 
			
		||||
    specifier: ^18
 | 
			
		||||
    version: 18.2.0(react@18.2.0)
 | 
			
		||||
  user-agents:
 | 
			
		||||
    specifier: ^1.1.156
 | 
			
		||||
    version: 1.1.156
 | 
			
		||||
 | 
			
		||||
devDependencies:
 | 
			
		||||
  '@types/node':
 | 
			
		||||
@ -25,6 +34,9 @@ devDependencies:
 | 
			
		||||
  '@types/react-dom':
 | 
			
		||||
    specifier: ^18
 | 
			
		||||
    version: 18.2.22
 | 
			
		||||
  '@types/user-agents':
 | 
			
		||||
    specifier: ^1.0.4
 | 
			
		||||
    version: 1.0.4
 | 
			
		||||
  autoprefixer:
 | 
			
		||||
    specifier: ^10.0.1
 | 
			
		||||
    version: 10.4.19(postcss@8.4.38)
 | 
			
		||||
@ -322,6 +334,10 @@ packages:
 | 
			
		||||
      csstype: 3.1.3
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /@types/user-agents@1.0.4:
 | 
			
		||||
    resolution: {integrity: sha512-AjeFc4oX5WPPflgKfRWWJfkEk7Wu82fnj1rROPsiqFt6yElpdGFg8Srtm/4PU4rA9UiDUZlruGPgcwTMQlwq4w==, tarball: https://registry.npmmirror.com/@types/user-agents/-/user-agents-1.0.4.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3):
 | 
			
		||||
    resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==, tarball: https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-6.21.0.tgz}
 | 
			
		||||
    engines: {node: ^16.0.0 || >=18.0.0}
 | 
			
		||||
@ -390,6 +406,13 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==, tarball: https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /abort-controller@3.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==, tarball: https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz}
 | 
			
		||||
    engines: {node: '>=6.5'}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      event-target-shim: 5.0.1
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /acorn-jsx@5.3.2(acorn@8.11.3):
 | 
			
		||||
    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, tarball: https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
@ -567,6 +590,15 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==, tarball: https://registry.npmmirror.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /asynckit@0.4.0:
 | 
			
		||||
    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, tarball: https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /atomic-sleep@1.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==, tarball: https://registry.npmmirror.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz}
 | 
			
		||||
    engines: {node: '>=8.0.0'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /autoprefixer@10.4.19(postcss@8.4.38):
 | 
			
		||||
    resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==, tarball: https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.19.tgz}
 | 
			
		||||
    engines: {node: ^10 || ^12 || >=14}
 | 
			
		||||
@ -595,6 +627,16 @@ packages:
 | 
			
		||||
    engines: {node: '>=4'}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /axios@1.6.8:
 | 
			
		||||
    resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==, tarball: https://registry.npmmirror.com/axios/-/axios-1.6.8.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      follow-redirects: 1.15.6
 | 
			
		||||
      form-data: 4.0.0
 | 
			
		||||
      proxy-from-env: 1.1.0
 | 
			
		||||
    transitivePeerDependencies:
 | 
			
		||||
      - debug
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /axobject-query@3.2.1:
 | 
			
		||||
    resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==, tarball: https://registry.npmmirror.com/axobject-query/-/axobject-query-3.2.1.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
@ -605,6 +647,10 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, tarball: https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /base64-js@1.5.1:
 | 
			
		||||
    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==, tarball: https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /binary-extensions@2.3.0:
 | 
			
		||||
    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==, tarball: https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz}
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
@ -641,6 +687,13 @@ packages:
 | 
			
		||||
      update-browserslist-db: 1.0.13(browserslist@4.23.0)
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /buffer@6.0.3:
 | 
			
		||||
    resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==, tarball: https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      base64-js: 1.5.1
 | 
			
		||||
      ieee754: 1.2.1
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /busboy@1.6.0:
 | 
			
		||||
    resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==, tarball: https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz}
 | 
			
		||||
    engines: {node: '>=10.16.0'}
 | 
			
		||||
@ -710,6 +763,13 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, tarball: https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /combined-stream@1.0.8:
 | 
			
		||||
    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, tarball: https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz}
 | 
			
		||||
    engines: {node: '>= 0.8'}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      delayed-stream: 1.0.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /commander@4.1.1:
 | 
			
		||||
    resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, tarball: https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz}
 | 
			
		||||
    engines: {node: '>= 6'}
 | 
			
		||||
@ -814,6 +874,11 @@ packages:
 | 
			
		||||
      object-keys: 1.1.1
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /delayed-stream@1.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, tarball: https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz}
 | 
			
		||||
    engines: {node: '>=0.4.0'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /dequal@2.0.3:
 | 
			
		||||
    resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, tarball: https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz}
 | 
			
		||||
    engines: {node: '>=6'}
 | 
			
		||||
@ -1274,6 +1339,16 @@ packages:
 | 
			
		||||
    engines: {node: '>=0.10.0'}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /event-target-shim@5.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==, tarball: https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz}
 | 
			
		||||
    engines: {node: '>=6'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /events@3.3.0:
 | 
			
		||||
    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==, tarball: https://registry.npmmirror.com/events/-/events-3.3.0.tgz}
 | 
			
		||||
    engines: {node: '>=0.8.x'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /fast-deep-equal@3.1.3:
 | 
			
		||||
    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, tarball: https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
@ -1297,6 +1372,11 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, tarball: https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /fast-redact@3.5.0:
 | 
			
		||||
    resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==, tarball: https://registry.npmmirror.com/fast-redact/-/fast-redact-3.5.0.tgz}
 | 
			
		||||
    engines: {node: '>=6'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /fastq@1.17.1:
 | 
			
		||||
    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==, tarball: https://registry.npmmirror.com/fastq/-/fastq-1.17.1.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
@ -1338,6 +1418,16 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==, tarball: https://registry.npmmirror.com/flatted/-/flatted-3.3.1.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /follow-redirects@1.15.6:
 | 
			
		||||
    resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==, tarball: https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz}
 | 
			
		||||
    engines: {node: '>=4.0'}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      debug: '*'
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      debug:
 | 
			
		||||
        optional: true
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /for-each@0.3.3:
 | 
			
		||||
    resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==, tarball: https://registry.npmmirror.com/for-each/-/for-each-0.3.3.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
@ -1352,6 +1442,15 @@ packages:
 | 
			
		||||
      signal-exit: 4.1.0
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /form-data@4.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==, tarball: https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz}
 | 
			
		||||
    engines: {node: '>= 6'}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      asynckit: 0.4.0
 | 
			
		||||
      combined-stream: 1.0.8
 | 
			
		||||
      mime-types: 2.1.35
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /fraction.js@4.3.7:
 | 
			
		||||
    resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==, tarball: https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
@ -1527,6 +1626,10 @@ packages:
 | 
			
		||||
      function-bind: 1.1.2
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /ieee754@1.2.1:
 | 
			
		||||
    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, tarball: https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /ignore@5.3.1:
 | 
			
		||||
    resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==, tarball: https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz}
 | 
			
		||||
    engines: {node: '>= 4'}
 | 
			
		||||
@ -1860,6 +1963,10 @@ packages:
 | 
			
		||||
      p-locate: 5.0.0
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /lodash.clonedeep@4.5.0:
 | 
			
		||||
    resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==, tarball: https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /lodash.merge@4.6.2:
 | 
			
		||||
    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, tarball: https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
@ -1895,6 +2002,18 @@ packages:
 | 
			
		||||
      picomatch: 2.3.1
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /mime-db@1.52.0:
 | 
			
		||||
    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, tarball: https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz}
 | 
			
		||||
    engines: {node: '>= 0.6'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /mime-types@2.1.35:
 | 
			
		||||
    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, tarball: https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz}
 | 
			
		||||
    engines: {node: '>= 0.6'}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      mime-db: 1.52.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /minimatch@3.1.2:
 | 
			
		||||
    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, tarball: https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
@ -2070,6 +2189,11 @@ packages:
 | 
			
		||||
      es-object-atoms: 1.0.0
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /on-exit-leak-free@2.1.2:
 | 
			
		||||
    resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==, tarball: https://registry.npmmirror.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz}
 | 
			
		||||
    engines: {node: '>=14.0.0'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /once@1.4.0:
 | 
			
		||||
    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, tarball: https://registry.npmmirror.com/once/-/once-1.4.0.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
@ -2154,6 +2278,34 @@ packages:
 | 
			
		||||
    engines: {node: '>=0.10.0'}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /pino-abstract-transport@1.1.0:
 | 
			
		||||
    resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==, tarball: https://registry.npmmirror.com/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      readable-stream: 4.5.2
 | 
			
		||||
      split2: 4.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /pino-std-serializers@6.2.2:
 | 
			
		||||
    resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==, tarball: https://registry.npmmirror.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /pino@8.19.0:
 | 
			
		||||
    resolution: {integrity: sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==, tarball: https://registry.npmmirror.com/pino/-/pino-8.19.0.tgz}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      atomic-sleep: 1.0.0
 | 
			
		||||
      fast-redact: 3.5.0
 | 
			
		||||
      on-exit-leak-free: 2.1.2
 | 
			
		||||
      pino-abstract-transport: 1.1.0
 | 
			
		||||
      pino-std-serializers: 6.2.2
 | 
			
		||||
      process-warning: 3.0.0
 | 
			
		||||
      quick-format-unescaped: 4.0.4
 | 
			
		||||
      real-require: 0.2.0
 | 
			
		||||
      safe-stable-stringify: 2.4.3
 | 
			
		||||
      sonic-boom: 3.8.0
 | 
			
		||||
      thread-stream: 2.4.1
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /pirates@4.0.6:
 | 
			
		||||
    resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==, tarball: https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz}
 | 
			
		||||
    engines: {node: '>= 6'}
 | 
			
		||||
@ -2248,6 +2400,15 @@ packages:
 | 
			
		||||
    engines: {node: '>= 0.8.0'}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /process-warning@3.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==, tarball: https://registry.npmmirror.com/process-warning/-/process-warning-3.0.0.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /process@0.11.10:
 | 
			
		||||
    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==, tarball: https://registry.npmmirror.com/process/-/process-0.11.10.tgz}
 | 
			
		||||
    engines: {node: '>= 0.6.0'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /prop-types@15.8.1:
 | 
			
		||||
    resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==, tarball: https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
@ -2256,6 +2417,10 @@ packages:
 | 
			
		||||
      react-is: 16.13.1
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /proxy-from-env@1.1.0:
 | 
			
		||||
    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==, tarball: https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /punycode@2.3.1:
 | 
			
		||||
    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, tarball: https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz}
 | 
			
		||||
    engines: {node: '>=6'}
 | 
			
		||||
@ -2265,6 +2430,10 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, tarball: https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /quick-format-unescaped@4.0.4:
 | 
			
		||||
    resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==, tarball: https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /react-dom@18.2.0(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==, tarball: https://registry.npmmirror.com/react-dom/-/react-dom-18.2.0.tgz}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
@ -2292,6 +2461,17 @@ packages:
 | 
			
		||||
      pify: 2.3.0
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /readable-stream@4.5.2:
 | 
			
		||||
    resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==, tarball: https://registry.npmmirror.com/readable-stream/-/readable-stream-4.5.2.tgz}
 | 
			
		||||
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      abort-controller: 3.0.0
 | 
			
		||||
      buffer: 6.0.3
 | 
			
		||||
      events: 3.3.0
 | 
			
		||||
      process: 0.11.10
 | 
			
		||||
      string_decoder: 1.3.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /readdirp@3.6.0:
 | 
			
		||||
    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, tarball: https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz}
 | 
			
		||||
    engines: {node: '>=8.10.0'}
 | 
			
		||||
@ -2299,6 +2479,11 @@ packages:
 | 
			
		||||
      picomatch: 2.3.1
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /real-require@0.2.0:
 | 
			
		||||
    resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==, tarball: https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz}
 | 
			
		||||
    engines: {node: '>= 12.13.0'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /reflect.getprototypeof@1.0.6:
 | 
			
		||||
    resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==, tarball: https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz}
 | 
			
		||||
    engines: {node: '>= 0.4'}
 | 
			
		||||
@ -2381,6 +2566,10 @@ packages:
 | 
			
		||||
      isarray: 2.0.5
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /safe-buffer@5.2.1:
 | 
			
		||||
    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, tarball: https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /safe-regex-test@1.0.3:
 | 
			
		||||
    resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==, tarball: https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz}
 | 
			
		||||
    engines: {node: '>= 0.4'}
 | 
			
		||||
@ -2390,6 +2579,11 @@ packages:
 | 
			
		||||
      is-regex: 1.1.4
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /safe-stable-stringify@2.4.3:
 | 
			
		||||
    resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==, tarball: https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz}
 | 
			
		||||
    engines: {node: '>=10'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /scheduler@0.23.0:
 | 
			
		||||
    resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==, tarball: https://registry.npmmirror.com/scheduler/-/scheduler-0.23.0.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
@ -2463,10 +2657,21 @@ packages:
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /sonic-boom@3.8.0:
 | 
			
		||||
    resolution: {integrity: sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==, tarball: https://registry.npmmirror.com/sonic-boom/-/sonic-boom-3.8.0.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      atomic-sleep: 1.0.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /source-map-js@1.2.0:
 | 
			
		||||
    resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==, tarball: https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz}
 | 
			
		||||
    engines: {node: '>=0.10.0'}
 | 
			
		||||
 | 
			
		||||
  /split2@4.2.0:
 | 
			
		||||
    resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==, tarball: https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz}
 | 
			
		||||
    engines: {node: '>= 10.x'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /streamsearch@1.1.0:
 | 
			
		||||
    resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==, tarball: https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz}
 | 
			
		||||
    engines: {node: '>=10.0.0'}
 | 
			
		||||
@ -2535,6 +2740,12 @@ packages:
 | 
			
		||||
      es-object-atoms: 1.0.0
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /string_decoder@1.3.0:
 | 
			
		||||
    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, tarball: https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      safe-buffer: 5.2.1
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /strip-ansi@6.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, tarball: https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz}
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
@ -2655,6 +2866,12 @@ packages:
 | 
			
		||||
      any-promise: 1.3.0
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /thread-stream@2.4.1:
 | 
			
		||||
    resolution: {integrity: sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==, tarball: https://registry.npmmirror.com/thread-stream/-/thread-stream-2.4.1.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      real-require: 0.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /to-regex-range@5.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, tarball: https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz}
 | 
			
		||||
    engines: {node: '>=8.0'}
 | 
			
		||||
@ -2780,6 +2997,12 @@ packages:
 | 
			
		||||
      punycode: 2.3.1
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /user-agents@1.1.156:
 | 
			
		||||
    resolution: {integrity: sha512-gphkDF41zQ/JGHc4hv2n05/okucmb7qYlhN2BKur0IKd1pkgXhS6l1GILoMQ/Hak7Z5FP3+X+0h7QcGF/pgxXw==, tarball: https://registry.npmmirror.com/user-agents/-/user-agents-1.1.156.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      lodash.clonedeep: 4.5.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /util-deprecate@1.0.2:
 | 
			
		||||
    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, tarball: https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								src/app/api/custom_generate/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/app/api/custom_generate/route.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
import { NextResponse, NextRequest } from "next/server";
 | 
			
		||||
import SunoApi from '@/lib/sunoApi';
 | 
			
		||||
 | 
			
		||||
export async function POST(req: NextRequest) {
 | 
			
		||||
    if (req.method === 'POST') {
 | 
			
		||||
        try {
 | 
			
		||||
            const body = await req.json();
 | 
			
		||||
            const { prompt, tags, title, make_instrumental, wait_audio } = body;
 | 
			
		||||
 | 
			
		||||
            // 校验输入参数
 | 
			
		||||
            if (!prompt || !tags || !title) {
 | 
			
		||||
                return new NextResponse(JSON.stringify({ error: 'Prompt, tags, and title are required' }), {
 | 
			
		||||
                    status: 400,
 | 
			
		||||
                    headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 调用 SunoApi.custom_generate 方法生成定制音频
 | 
			
		||||
            const audioInfo = await SunoApi.custom_generate(
 | 
			
		||||
                prompt, tags, title,
 | 
			
		||||
                make_instrumental == true,
 | 
			
		||||
                wait_audio == true
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // 使用 NextResponse 构建成功响应
 | 
			
		||||
            return new NextResponse(JSON.stringify(audioInfo), {
 | 
			
		||||
                status: 200,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
        } catch (error: any) {
 | 
			
		||||
            console.error('Error generating custom audio:', error.response.data);
 | 
			
		||||
            if (error.response.status === 402) {
 | 
			
		||||
                return new NextResponse(JSON.stringify({ error: error.response.data.detail }), {
 | 
			
		||||
                    status: 402,
 | 
			
		||||
                    headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            // 使用 NextResponse 构建错误响应
 | 
			
		||||
            return new NextResponse(JSON.stringify({ error: 'Internal server error' }), {
 | 
			
		||||
                status: 500,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return new NextResponse('Method Not Allowed', {
 | 
			
		||||
            headers: { Allow: 'POST' },
 | 
			
		||||
            status: 405
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								src/app/api/generate/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/app/api/generate/route.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
import { NextResponse, NextRequest } from "next/server";
 | 
			
		||||
import SunoApi from '@/lib/sunoApi';
 | 
			
		||||
 | 
			
		||||
export async function POST(req: NextRequest) {
 | 
			
		||||
    if (req.method === 'POST') {
 | 
			
		||||
        try {
 | 
			
		||||
            const body = await req.json();
 | 
			
		||||
            const { prompt, make_instrumental, wait_audio } = body;
 | 
			
		||||
 | 
			
		||||
            // 校验输入参数
 | 
			
		||||
            if (!prompt) {
 | 
			
		||||
                return new NextResponse(JSON.stringify({ error: 'Prompt is required' }), {
 | 
			
		||||
                    status: 400,
 | 
			
		||||
                    headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 调用 SunoApi.generate 方法生成音频
 | 
			
		||||
            const audioInfo = await SunoApi.generate(prompt, make_instrumental == true, wait_audio == true);
 | 
			
		||||
 | 
			
		||||
            // 使用 NextResponse 构建成功响应
 | 
			
		||||
            return new NextResponse(JSON.stringify(audioInfo), {
 | 
			
		||||
                status: 200,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
        } catch (error: any) {
 | 
			
		||||
            console.error('Error generating custom audio:', JSON.stringify(error.response.data));
 | 
			
		||||
            if (error.response.status === 402) {
 | 
			
		||||
                return new NextResponse(JSON.stringify({ error: error.response.data.detail }), {
 | 
			
		||||
                    status: 402,
 | 
			
		||||
                    headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            // 使用 NextResponse 构建错误响应
 | 
			
		||||
            return new NextResponse(JSON.stringify({ error: 'Internal server error: ' + JSON.stringify(error.response.data.detail) }), {
 | 
			
		||||
                status: 500,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return new NextResponse('Method Not Allowed', {
 | 
			
		||||
            headers: { Allow: 'POST' },
 | 
			
		||||
            status: 405
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/app/api/get/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/app/api/get/route.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
import { NextResponse, NextRequest } from "next/server";
 | 
			
		||||
import SunoApi from '@/lib/sunoApi';
 | 
			
		||||
 | 
			
		||||
export async function GET(req: NextRequest) {
 | 
			
		||||
    if (req.method === 'GET') {
 | 
			
		||||
        try {
 | 
			
		||||
            // 修复了获取查询参数的方式
 | 
			
		||||
            const url = new URL(req.url);
 | 
			
		||||
            const songIds = url.searchParams.get('ids');
 | 
			
		||||
            let audioInfo = [];
 | 
			
		||||
            if (songIds && songIds.length > 0) {
 | 
			
		||||
                const idsArray = songIds.split(',');
 | 
			
		||||
                // 调用 SunoApi.get 方法获取音频信息
 | 
			
		||||
                audioInfo = await SunoApi.get(idsArray);
 | 
			
		||||
            } else {
 | 
			
		||||
                audioInfo = await SunoApi.get();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 使用 NextResponse 构建成功响应
 | 
			
		||||
            return new NextResponse(JSON.stringify(audioInfo), {
 | 
			
		||||
                status: 200,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('Error fetching audio:', error);
 | 
			
		||||
            // 使用 NextResponse 构建错误响应
 | 
			
		||||
            return new NextResponse(JSON.stringify({ error: 'Internal server error' }), {
 | 
			
		||||
                status: 500,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return new NextResponse('Method Not Allowed', {
 | 
			
		||||
            headers: { Allow: 'GET' },
 | 
			
		||||
            status: 405
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								src/app/api/get_limit/route.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/app/api/get_limit/route.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
import { NextResponse, NextRequest } from "next/server";
 | 
			
		||||
import SunoApi from '@/lib/sunoApi';
 | 
			
		||||
 | 
			
		||||
export async function GET(req: NextRequest) {
 | 
			
		||||
    if (req.method === 'GET') {
 | 
			
		||||
        try {
 | 
			
		||||
            // 调用 SunoApi.get_limit 方法获取剩余的信用额度
 | 
			
		||||
            const limit = await SunoApi.get_limit();
 | 
			
		||||
 | 
			
		||||
            // 使用 NextResponse 构建成功响应
 | 
			
		||||
            return new NextResponse(JSON.stringify({ limit }), {
 | 
			
		||||
                status: 200,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('Error fetching limit:', error);
 | 
			
		||||
            // 使用 NextResponse 构建错误响应
 | 
			
		||||
            return new NextResponse(JSON.stringify({ error: 'Internal server error' }), {
 | 
			
		||||
                status: 500,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return new NextResponse('Method Not Allowed', {
 | 
			
		||||
            headers: { Allow: 'GET' },
 | 
			
		||||
            status: 405
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,285 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import UserAgent from 'user-agents';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface AudioInfo {
 | 
			
		||||
  id: string;
 | 
			
		||||
  title?: string;
 | 
			
		||||
  image_url?: string;
 | 
			
		||||
  lyric?: string;
 | 
			
		||||
  audio_url?: string;
 | 
			
		||||
  video_url?: string;
 | 
			
		||||
  created_at: string;
 | 
			
		||||
  model_name: string;
 | 
			
		||||
  gpt_description_prompt?: string;
 | 
			
		||||
  prompt?: string;
 | 
			
		||||
  status: string;
 | 
			
		||||
  type?: string;
 | 
			
		||||
  tags?: string;
 | 
			
		||||
  duration?: string;
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 暂停指定的秒数。
 | 
			
		||||
 * @param x 最小秒数。
 | 
			
		||||
 * @param y 最大秒数(可选)。
 | 
			
		||||
 */
 | 
			
		||||
const sleep = (x: number, y?: number): Promise<void> => {
 | 
			
		||||
  let timeout = x * 1000;
 | 
			
		||||
  if (y !== undefined && y !== x) {
 | 
			
		||||
    const min = Math.min(x, y);
 | 
			
		||||
    const max = Math.max(x, y);
 | 
			
		||||
    timeout = Math.floor(Math.random() * (max - min + 1) + min) * 1000;
 | 
			
		||||
  }
 | 
			
		||||
  console.log(`Sleeping for ${timeout / 1000} seconds`);
 | 
			
		||||
  return new Promise(resolve => setTimeout(resolve, timeout));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SunoApi {
 | 
			
		||||
  private static baseUrl: string = 'https://studio-api.suno.ai';
 | 
			
		||||
  private static clerkBaseUrl: string = 'https://clerk.suno.ai';
 | 
			
		||||
  private static cookie: string = process.env.SUNO_COOKIE || '';
 | 
			
		||||
  private static userAgent: string = new UserAgent().toString();
 | 
			
		||||
  private static sid: string | null = null;
 | 
			
		||||
 | 
			
		||||
  private static async getAuthToken(): Promise<string> {
 | 
			
		||||
 | 
			
		||||
    // 获取会话ID的URL
 | 
			
		||||
    const getSessionUrl = `${SunoApi.clerkBaseUrl}/v1/client?_clerk_js_version=4.70.5`;
 | 
			
		||||
    // 交换令牌的URL模板
 | 
			
		||||
    const exchangeTokenUrlTemplate = `${SunoApi.clerkBaseUrl}/v1/client/sessions/{sid}/tokens/api?_clerk_js_version=4.70.0`;
 | 
			
		||||
 | 
			
		||||
    // 获取会话ID
 | 
			
		||||
    const sessionResponse = await axios.get(getSessionUrl, {
 | 
			
		||||
      headers: {
 | 
			
		||||
        'User-Agent': SunoApi.userAgent,
 | 
			
		||||
        'Cookie': SunoApi.cookie,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    const sid = sessionResponse.data.response?.last_active_session_id;
 | 
			
		||||
    if (!sid) {
 | 
			
		||||
      throw new Error("Failed to get session id");
 | 
			
		||||
    }
 | 
			
		||||
    console.log(`Successfully retrieved session ID: ${sid}`);
 | 
			
		||||
    SunoApi.sid = sid; // 保存会话ID以备后用
 | 
			
		||||
 | 
			
		||||
    // 使用会话ID获取JWT令牌
 | 
			
		||||
    const exchangeTokenUrl = exchangeTokenUrlTemplate.replace('{sid}', sid);
 | 
			
		||||
    // console.log("Exchange Token URL:\n", exchangeTokenUrl);
 | 
			
		||||
    // console.log("Exchange User-Agent:\n", SunoApi.userAgent);
 | 
			
		||||
    // console.log("Exchange Cookie:\n", SunoApi.cookie);
 | 
			
		||||
    const tokenResponse = await axios.post(
 | 
			
		||||
      exchangeTokenUrl,
 | 
			
		||||
      {},
 | 
			
		||||
      {
 | 
			
		||||
        headers: {
 | 
			
		||||
          'User-Agent': SunoApi.userAgent,
 | 
			
		||||
          'Cookie': SunoApi.cookie,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    console.log("Token Response:\n", JSON.stringify(tokenResponse.data, null, 2));
 | 
			
		||||
 | 
			
		||||
    return tokenResponse.data.jwt;
 | 
			
		||||
  }
 | 
			
		||||
  public static async KeepAlive(): Promise<void> {
 | 
			
		||||
    if (!SunoApi.sid) {
 | 
			
		||||
      throw new Error("Session ID is not set. Cannot renew token.");
 | 
			
		||||
    }
 | 
			
		||||
    // 续订会话令牌的URL
 | 
			
		||||
    const renewUrl = `${SunoApi.clerkBaseUrl}/v1/client/sessions/${SunoApi.sid}/tokens/api?_clerk_js_version=4.70.0`;
 | 
			
		||||
    // 续订会话令牌
 | 
			
		||||
    const renewResponse = await axios.post(
 | 
			
		||||
      renewUrl,
 | 
			
		||||
      {},
 | 
			
		||||
      {
 | 
			
		||||
        headers: {
 | 
			
		||||
          'User-Agent': SunoApi.userAgent,
 | 
			
		||||
          'Cookie': SunoApi.cookie,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    console.log("Renew Response:\n", JSON.stringify(renewResponse.data, null, 2));
 | 
			
		||||
    await sleep(1, 2);
 | 
			
		||||
    const newToken = renewResponse.data.jwt;
 | 
			
		||||
    // 更新请求头中的Authorization字段,使用新的JWT令牌
 | 
			
		||||
    axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static async generate(
 | 
			
		||||
    prompt: string,
 | 
			
		||||
    make_instrumental: boolean = false,
 | 
			
		||||
    wait_audio: boolean = false,
 | 
			
		||||
  ): Promise<AudioInfo[]> {
 | 
			
		||||
 | 
			
		||||
    const audios = this.generateSongs(prompt, false, undefined, undefined, make_instrumental, wait_audio);
 | 
			
		||||
    console.log("Custom Generate Response:\n", JSON.stringify(audios, null, 2));
 | 
			
		||||
    return audios;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static async custom_generate(
 | 
			
		||||
    prompt: string,
 | 
			
		||||
    tags: string,
 | 
			
		||||
    title: string,
 | 
			
		||||
    make_instrumental: boolean = false,
 | 
			
		||||
    wait_audio: boolean = false,
 | 
			
		||||
  ): Promise<AudioInfo[]> {
 | 
			
		||||
 | 
			
		||||
    const audios = await this.generateSongs(prompt, true, tags, title, make_instrumental, wait_audio);
 | 
			
		||||
    console.log("Custom Generate Response:\n", JSON.stringify(audios, null, 2));
 | 
			
		||||
    return audios;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static async generateSongs(
 | 
			
		||||
    prompt: string,
 | 
			
		||||
    isCustom: boolean,
 | 
			
		||||
    tags?: string,
 | 
			
		||||
    title?: string,
 | 
			
		||||
    make_instrumental?: boolean,
 | 
			
		||||
    wait_audio: boolean = false
 | 
			
		||||
  ): Promise<AudioInfo[]> {
 | 
			
		||||
    const authToken = await this.getAuthToken();
 | 
			
		||||
    const payload: any = {
 | 
			
		||||
      make_instrumental: make_instrumental == true,
 | 
			
		||||
      mv: "chirp-v3-0",
 | 
			
		||||
      prompt: "",
 | 
			
		||||
    };
 | 
			
		||||
    if (isCustom) {
 | 
			
		||||
      payload.tags = tags;
 | 
			
		||||
      payload.title = title;
 | 
			
		||||
      payload.prompt = prompt;
 | 
			
		||||
    } else {
 | 
			
		||||
      payload.gpt_description_prompt = prompt;
 | 
			
		||||
    }
 | 
			
		||||
    console.log("generateSongs payload:\n", {
 | 
			
		||||
      prompt: prompt,
 | 
			
		||||
      isCustom: isCustom,
 | 
			
		||||
      tags: tags,
 | 
			
		||||
      title: title,
 | 
			
		||||
      make_instrumental: make_instrumental,
 | 
			
		||||
      wait_audio: wait_audio,
 | 
			
		||||
      payload: payload,
 | 
			
		||||
    });
 | 
			
		||||
    const response = await axios.post(
 | 
			
		||||
      `${SunoApi.baseUrl}/api/generate/v2/`,
 | 
			
		||||
      payload,
 | 
			
		||||
      {
 | 
			
		||||
        headers: {
 | 
			
		||||
          'Authorization': `Bearer ${authToken}`,
 | 
			
		||||
          'User-Agent': SunoApi.userAgent,
 | 
			
		||||
        },
 | 
			
		||||
        timeout: 10000, // 10 seconds timeout
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    console.log("generateSongs Response:\n", JSON.stringify(response.data, null, 2));
 | 
			
		||||
    if (response.status !== 200) {
 | 
			
		||||
      throw new Error("Error response:" + response.statusText);
 | 
			
		||||
    }
 | 
			
		||||
    const songIds = response.data.clips.map((audio: any) => audio.id);
 | 
			
		||||
    //Want to wait for music file generation
 | 
			
		||||
    if (wait_audio === true) {
 | 
			
		||||
      const startTime = Date.now();
 | 
			
		||||
      let lastResponse: AudioInfo[] = [];
 | 
			
		||||
      await sleep(2, 4);
 | 
			
		||||
      while (Date.now() - startTime < 30000) {
 | 
			
		||||
        const response = await SunoApi.get(songIds);
 | 
			
		||||
        console.log("Waiting for audio Response:\n", JSON.stringify(response, null, 2));
 | 
			
		||||
        const allCompleted = response.every(
 | 
			
		||||
          audio => audio.status === 'streaming' || audio.status === 'complete'
 | 
			
		||||
        );
 | 
			
		||||
        if (allCompleted) {
 | 
			
		||||
          return response;
 | 
			
		||||
        }
 | 
			
		||||
        lastResponse = response;
 | 
			
		||||
        await sleep(2, 4);
 | 
			
		||||
        this.KeepAlive();
 | 
			
		||||
      }
 | 
			
		||||
      return lastResponse;
 | 
			
		||||
    } else {
 | 
			
		||||
      this.KeepAlive();
 | 
			
		||||
      return response.data.clips.map((audio: any) => ({
 | 
			
		||||
        id: audio.id,
 | 
			
		||||
        title: audio.title,
 | 
			
		||||
        image_url: audio.image_url,
 | 
			
		||||
        lyric: audio.metadata.prompt,
 | 
			
		||||
        audio_url: audio.audio_url,
 | 
			
		||||
        video_url: audio.video_url,
 | 
			
		||||
        created_at: audio.created_at,
 | 
			
		||||
        model_name: audio.model_name,
 | 
			
		||||
        status: audio.status,
 | 
			
		||||
        gpt_description_prompt: audio.metadata.gpt_description_prompt,
 | 
			
		||||
        prompt: audio.metadata.prompt,
 | 
			
		||||
        type: audio.metadata.type,
 | 
			
		||||
        tags: audio.metadata.tags,
 | 
			
		||||
        duration: audio.metadata.duration_formatted,
 | 
			
		||||
      }));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
     * 将音频元数据中的歌词(prompt)处理成易于阅读的格式。
 | 
			
		||||
     * @param prompt 原始歌词文本。
 | 
			
		||||
     * @returns 处理后的歌词文本。
 | 
			
		||||
     */
 | 
			
		||||
  private static parseLyrics(prompt: string): string {
 | 
			
		||||
    // 假设原始歌词是以特定分隔符(例如,换行符)分隔的,我们可以将其转换为更易于阅读的格式。
 | 
			
		||||
    // 这里的实现可以根据实际的歌词格式进行调整。
 | 
			
		||||
    // 例如,如果歌词是以连续的文本形式存在,可能需要根据特定的标记(如句号、逗号等)来分割。
 | 
			
		||||
    // 下面的实现假设歌词已经是以换行符分隔的。
 | 
			
		||||
 | 
			
		||||
    // 使用换行符分割歌词,并确保移除空行。
 | 
			
		||||
    const lines = prompt.split('\n').filter(line => line.trim() !== '');
 | 
			
		||||
 | 
			
		||||
    // 将处理后的歌词行重新组合成一个字符串,每行之间用换行符分隔。
 | 
			
		||||
    // 可以在这里添加额外的格式化逻辑,如添加特定的标记或者处理特殊的行。
 | 
			
		||||
    const formattedLyrics = lines.join('\n');
 | 
			
		||||
 | 
			
		||||
    return formattedLyrics;
 | 
			
		||||
  }
 | 
			
		||||
  public static async get(songIds?: string[]): Promise<AudioInfo[]> {
 | 
			
		||||
    const authToken = await this.getAuthToken();
 | 
			
		||||
    let url = `${SunoApi.baseUrl}/api/feed/`;
 | 
			
		||||
    if (songIds) {
 | 
			
		||||
      url = `${url}?ids=${songIds.join(',')}`;
 | 
			
		||||
    }
 | 
			
		||||
    console.log("Get URL:\n", url);
 | 
			
		||||
    const response = await axios.get(url, {
 | 
			
		||||
      headers: {
 | 
			
		||||
        'Authorization': `Bearer ${authToken}`,
 | 
			
		||||
        'User-Agent': SunoApi.userAgent,
 | 
			
		||||
      },
 | 
			
		||||
      timeout: 3000, // 3 seconds timeout
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const audios = response.data;
 | 
			
		||||
    console.log("Get Response:\n", JSON.stringify(audios, null, 2));
 | 
			
		||||
    return audios.map((audio: any) => ({
 | 
			
		||||
      id: audio.id,
 | 
			
		||||
      title: audio.title,
 | 
			
		||||
      image_url: audio.image_url,
 | 
			
		||||
      lyric: audio.metadata.prompt ? this.parseLyrics(audio.metadata.prompt) : "",
 | 
			
		||||
      audio_url: audio.audio_url,
 | 
			
		||||
      video_url: audio.video_url,
 | 
			
		||||
      created_at: audio.created_at,
 | 
			
		||||
      model_name: audio.model_name,
 | 
			
		||||
      status: audio.status,
 | 
			
		||||
      gpt_description_prompt: audio.metadata.gpt_description_prompt,
 | 
			
		||||
      prompt: audio.metadata.prompt,
 | 
			
		||||
      type: audio.metadata.type,
 | 
			
		||||
      tags: audio.metadata.tags,
 | 
			
		||||
      duration: audio.metadata.duration_formatted,
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static async get_limit(): Promise<number> {
 | 
			
		||||
    const authToken = await this.getAuthToken();
 | 
			
		||||
    const response = await axios.get(`${SunoApi.baseUrl}/api/billing/info/`, {
 | 
			
		||||
      headers: {
 | 
			
		||||
        'Authorization': `Bearer ${authToken}`,
 | 
			
		||||
        'User-Agent': SunoApi.userAgent,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    return response.data.total_credits_left;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default SunoApi;
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user