feat: Add pino-pretty logging and update SunoApi methods
Introduce pino-pretty for improved logging and update SunoApi methods to incorporate new functionalities for generating custom audios, retrieving audio information, and obtaining credit details. The new pino-pretty logging enhances readability and provides better insights into application behavior. These changes align with the broader goal of enhancing developer experience and maintaining application stability. #N/A
This commit is contained in:
		
							parent
							
								
									0fcf77df6d
								
							
						
					
					
						commit
						2a309f87a7
					
				@ -18,6 +18,7 @@
 | 
			
		||||
    "axios": "^1.6.8",
 | 
			
		||||
    "next": "14.1.4",
 | 
			
		||||
    "pino": "^8.19.0",
 | 
			
		||||
    "pino-pretty": "^11.0.0",
 | 
			
		||||
    "react": "^18",
 | 
			
		||||
    "react-dom": "^18",
 | 
			
		||||
    "user-agents": "^1.1.156"
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,9 @@ dependencies:
 | 
			
		||||
  pino:
 | 
			
		||||
    specifier: ^8.19.0
 | 
			
		||||
    version: 8.19.0
 | 
			
		||||
  pino-pretty:
 | 
			
		||||
    specifier: ^11.0.0
 | 
			
		||||
    version: 11.0.0
 | 
			
		||||
  react:
 | 
			
		||||
    specifier: ^18
 | 
			
		||||
    version: 18.2.0
 | 
			
		||||
@ -763,6 +766,10 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, tarball: https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /colorette@2.0.20:
 | 
			
		||||
    resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==, tarball: https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /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'}
 | 
			
		||||
@ -829,6 +836,10 @@ packages:
 | 
			
		||||
      is-data-view: 1.0.1
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /dateformat@4.6.3:
 | 
			
		||||
    resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==, tarball: https://registry.npmmirror.com/dateformat/-/dateformat-4.6.3.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /debug@3.2.7:
 | 
			
		||||
    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==, tarball: https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
@ -929,6 +940,12 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, tarball: https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /end-of-stream@1.4.4:
 | 
			
		||||
    resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==, tarball: https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      once: 1.4.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /enhanced-resolve@5.16.0:
 | 
			
		||||
    resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==, tarball: https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz}
 | 
			
		||||
    engines: {node: '>=10.13.0'}
 | 
			
		||||
@ -1349,6 +1366,10 @@ packages:
 | 
			
		||||
    engines: {node: '>=0.8.x'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /fast-copy@3.0.2:
 | 
			
		||||
    resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==, tarball: https://registry.npmmirror.com/fast-copy/-/fast-copy-3.0.2.tgz}
 | 
			
		||||
    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
 | 
			
		||||
@ -1377,6 +1398,10 @@ packages:
 | 
			
		||||
    engines: {node: '>=6'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /fast-safe-stringify@2.1.1:
 | 
			
		||||
    resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==, tarball: https://registry.npmmirror.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /fastq@1.17.1:
 | 
			
		||||
    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==, tarball: https://registry.npmmirror.com/fastq/-/fastq-1.17.1.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
@ -1626,6 +1651,10 @@ packages:
 | 
			
		||||
      function-bind: 1.1.2
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /help-me@5.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==, tarball: https://registry.npmmirror.com/help-me/-/help-me-5.0.0.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /ieee754@1.2.1:
 | 
			
		||||
    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, tarball: https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
@ -1878,6 +1907,11 @@ packages:
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /joycon@3.1.1:
 | 
			
		||||
    resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==, tarball: https://registry.npmmirror.com/joycon/-/joycon-3.1.1.tgz}
 | 
			
		||||
    engines: {node: '>=10'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /js-tokens@4.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, tarball: https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz}
 | 
			
		||||
 | 
			
		||||
@ -2029,7 +2063,6 @@ packages:
 | 
			
		||||
 | 
			
		||||
  /minimist@1.2.8:
 | 
			
		||||
    resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, tarball: https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /minipass@7.0.4:
 | 
			
		||||
    resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==, tarball: https://registry.npmmirror.com/minipass/-/minipass-7.0.4.tgz}
 | 
			
		||||
@ -2198,7 +2231,6 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, tarball: https://registry.npmmirror.com/once/-/once-1.4.0.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      wrappy: 1.0.2
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /optionator@0.9.3:
 | 
			
		||||
    resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==, tarball: https://registry.npmmirror.com/optionator/-/optionator-0.9.3.tgz}
 | 
			
		||||
@ -2285,6 +2317,26 @@ packages:
 | 
			
		||||
      split2: 4.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /pino-pretty@11.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-YFJZqw59mHIY72wBnBs7XhLGG6qpJMa4pEQTRgEPEbjIYbng2LXEZZF1DoyDg9CfejEy8uZCyzpcBXXG0oOCwQ==, tarball: https://registry.npmmirror.com/pino-pretty/-/pino-pretty-11.0.0.tgz}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      colorette: 2.0.20
 | 
			
		||||
      dateformat: 4.6.3
 | 
			
		||||
      fast-copy: 3.0.2
 | 
			
		||||
      fast-safe-stringify: 2.1.1
 | 
			
		||||
      help-me: 5.0.0
 | 
			
		||||
      joycon: 3.1.1
 | 
			
		||||
      minimist: 1.2.8
 | 
			
		||||
      on-exit-leak-free: 2.1.2
 | 
			
		||||
      pino-abstract-transport: 1.1.0
 | 
			
		||||
      pump: 3.0.0
 | 
			
		||||
      readable-stream: 4.5.2
 | 
			
		||||
      secure-json-parse: 2.7.0
 | 
			
		||||
      sonic-boom: 3.8.0
 | 
			
		||||
      strip-json-comments: 3.1.1
 | 
			
		||||
    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
 | 
			
		||||
@ -2421,6 +2473,13 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==, tarball: https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /pump@3.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==, tarball: https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      end-of-stream: 1.4.4
 | 
			
		||||
      once: 1.4.0
 | 
			
		||||
    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'}
 | 
			
		||||
@ -2590,6 +2649,10 @@ packages:
 | 
			
		||||
      loose-envify: 1.4.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /secure-json-parse@2.7.0:
 | 
			
		||||
    resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==, tarball: https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /semver@6.3.1:
 | 
			
		||||
    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, tarball: https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
@ -2768,7 +2831,6 @@ packages:
 | 
			
		||||
  /strip-json-comments@3.1.1:
 | 
			
		||||
    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, tarball: https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz}
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /styled-jsx@5.1.1(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==, tarball: https://registry.npmmirror.com/styled-jsx/-/styled-jsx-5.1.1.tgz}
 | 
			
		||||
@ -3084,7 +3146,6 @@ packages:
 | 
			
		||||
 | 
			
		||||
  /wrappy@1.0.2:
 | 
			
		||||
    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, tarball: https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /yallist@4.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, tarball: https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz}
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,10 @@ export async function GET(req: NextRequest) {
 | 
			
		||||
    if (req.method === 'GET') {
 | 
			
		||||
        try {
 | 
			
		||||
            // 调用 SunoApi.get_limit 方法获取剩余的信用额度
 | 
			
		||||
            const limit = await SunoApi.get_limit();
 | 
			
		||||
            const limit = await SunoApi.get_credits();
 | 
			
		||||
 | 
			
		||||
            // 使用 NextResponse 构建成功响应
 | 
			
		||||
            return new NextResponse(JSON.stringify({ limit }), {
 | 
			
		||||
            return new NextResponse(JSON.stringify(limit), {
 | 
			
		||||
                status: 200,
 | 
			
		||||
                headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import UserAgent from 'user-agents';
 | 
			
		||||
 | 
			
		||||
import pino from 'pino';
 | 
			
		||||
const logger = pino();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface AudioInfo {
 | 
			
		||||
@ -31,7 +32,9 @@ const sleep = (x: number, y?: number): Promise<void> => {
 | 
			
		||||
    const max = Math.max(x, y);
 | 
			
		||||
    timeout = Math.floor(Math.random() * (max - min + 1) + min) * 1000;
 | 
			
		||||
  }
 | 
			
		||||
  console.log(`Sleeping for ${timeout / 1000} seconds`);
 | 
			
		||||
  // console.log(`Sleeping for ${timeout / 1000} seconds`);
 | 
			
		||||
  logger.info(`Sleeping for ${timeout / 1000} seconds`);
 | 
			
		||||
 | 
			
		||||
  return new Promise(resolve => setTimeout(resolve, timeout));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -60,14 +63,10 @@ class SunoApi {
 | 
			
		||||
    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,
 | 
			
		||||
      {},
 | 
			
		||||
@ -78,8 +77,6 @@ class SunoApi {
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    console.log("Token Response:\n", JSON.stringify(tokenResponse.data, null, 2));
 | 
			
		||||
 | 
			
		||||
    return tokenResponse.data.jwt;
 | 
			
		||||
  }
 | 
			
		||||
  public static async KeepAlive(): Promise<void> {
 | 
			
		||||
@ -99,7 +96,7 @@ class SunoApi {
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    console.log("Renew Response:\n", JSON.stringify(renewResponse.data, null, 2));
 | 
			
		||||
    logger.info("KeepAlive...\n");
 | 
			
		||||
    await sleep(1, 2);
 | 
			
		||||
    const newToken = renewResponse.data.jwt;
 | 
			
		||||
    // 更新请求头中的Authorization字段,使用新的JWT令牌
 | 
			
		||||
@ -111,12 +108,24 @@ class SunoApi {
 | 
			
		||||
    make_instrumental: boolean = false,
 | 
			
		||||
    wait_audio: boolean = false,
 | 
			
		||||
  ): Promise<AudioInfo[]> {
 | 
			
		||||
 | 
			
		||||
    const startTime = Date.now();
 | 
			
		||||
    const audios = this.generateSongs(prompt, false, undefined, undefined, make_instrumental, wait_audio);
 | 
			
		||||
    console.log("Custom Generate Response:\n", JSON.stringify(audios, null, 2));
 | 
			
		||||
    const costTime = Date.now() - startTime;
 | 
			
		||||
    logger.info("Generate Response:\n", JSON.stringify(audios, null, 2));
 | 
			
		||||
    logger.info("Cost time: ", costTime);
 | 
			
		||||
    return audios;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Generates custom audio based on provided parameters.
 | 
			
		||||
   * 
 | 
			
		||||
   * @param prompt The text prompt to generate audio from.
 | 
			
		||||
   * @param tags Tags to categorize the generated audio.
 | 
			
		||||
   * @param title The title for the generated audio.
 | 
			
		||||
   * @param make_instrumental Indicates if the generated audio should be instrumental.
 | 
			
		||||
   * @param wait_audio Indicates if the method should wait for the audio file to be fully generated before returning.
 | 
			
		||||
   * @returns A promise that resolves to an array of AudioInfo objects representing the generated audios.
 | 
			
		||||
   */
 | 
			
		||||
  public static async custom_generate(
 | 
			
		||||
    prompt: string,
 | 
			
		||||
    tags: string,
 | 
			
		||||
@ -124,12 +133,25 @@ class SunoApi {
 | 
			
		||||
    make_instrumental: boolean = false,
 | 
			
		||||
    wait_audio: boolean = false,
 | 
			
		||||
  ): Promise<AudioInfo[]> {
 | 
			
		||||
 | 
			
		||||
    const startTime = Date.now();
 | 
			
		||||
    const audios = await this.generateSongs(prompt, true, tags, title, make_instrumental, wait_audio);
 | 
			
		||||
    console.log("Custom Generate Response:\n", JSON.stringify(audios, null, 2));
 | 
			
		||||
    const costTime = Date.now() - startTime;
 | 
			
		||||
    logger.info("Custom Generate Response:\n", JSON.stringify(audios, null, 2));
 | 
			
		||||
    logger.info("Cost time: ", costTime);
 | 
			
		||||
    return audios;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Generates songs based on the provided parameters.
 | 
			
		||||
   * 
 | 
			
		||||
   * @param prompt The text prompt to generate songs from.
 | 
			
		||||
   * @param isCustom Indicates if the generation should consider custom parameters like tags and title.
 | 
			
		||||
   * @param tags Optional tags to categorize the song, used only if isCustom is true.
 | 
			
		||||
   * @param title Optional title for the song, used only if isCustom is true.
 | 
			
		||||
   * @param make_instrumental Indicates if the generated song should be instrumental.
 | 
			
		||||
   * @param wait_audio Indicates if the method should wait for the audio file to be fully generated before returning.
 | 
			
		||||
   * @returns A promise that resolves to an array of AudioInfo objects representing the generated songs.
 | 
			
		||||
   */
 | 
			
		||||
  private static async generateSongs(
 | 
			
		||||
    prompt: string,
 | 
			
		||||
    isCustom: boolean,
 | 
			
		||||
@ -151,7 +173,7 @@ class SunoApi {
 | 
			
		||||
    } else {
 | 
			
		||||
      payload.gpt_description_prompt = prompt;
 | 
			
		||||
    }
 | 
			
		||||
    console.log("generateSongs payload:\n", {
 | 
			
		||||
    logger.info("generateSongs payload:\n", {
 | 
			
		||||
      prompt: prompt,
 | 
			
		||||
      isCustom: isCustom,
 | 
			
		||||
      tags: tags,
 | 
			
		||||
@ -171,7 +193,7 @@ class SunoApi {
 | 
			
		||||
        timeout: 10000, // 10 seconds timeout
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    console.log("generateSongs Response:\n", JSON.stringify(response.data, null, 2));
 | 
			
		||||
    logger.info("generateSongs Response:\n", JSON.stringify(response.data, null, 2));
 | 
			
		||||
    if (response.status !== 200) {
 | 
			
		||||
      throw new Error("Error response:" + response.statusText);
 | 
			
		||||
    }
 | 
			
		||||
@ -180,10 +202,9 @@ class SunoApi {
 | 
			
		||||
    if (wait_audio === true) {
 | 
			
		||||
      const startTime = Date.now();
 | 
			
		||||
      let lastResponse: AudioInfo[] = [];
 | 
			
		||||
      await sleep(2, 4);
 | 
			
		||||
      while (Date.now() - startTime < 30000) {
 | 
			
		||||
      await sleep(5, 5);
 | 
			
		||||
      while (Date.now() - startTime < 100000) {
 | 
			
		||||
        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'
 | 
			
		||||
        );
 | 
			
		||||
@ -191,7 +212,7 @@ class SunoApi {
 | 
			
		||||
          return response;
 | 
			
		||||
        }
 | 
			
		||||
        lastResponse = response;
 | 
			
		||||
        await sleep(2, 4);
 | 
			
		||||
        await sleep(3, 6);
 | 
			
		||||
        this.KeepAlive();
 | 
			
		||||
      }
 | 
			
		||||
      return lastResponse;
 | 
			
		||||
@ -216,32 +237,38 @@ class SunoApi {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
     * 将音频元数据中的歌词(prompt)处理成易于阅读的格式。
 | 
			
		||||
     * @param prompt 原始歌词文本。
 | 
			
		||||
     * @returns 处理后的歌词文本。
 | 
			
		||||
     * Processes the lyrics (prompt) from the audio metadata into a more readable format.
 | 
			
		||||
     * @param prompt The original lyrics text.
 | 
			
		||||
     * @returns The processed lyrics text.
 | 
			
		||||
     */
 | 
			
		||||
  private static parseLyrics(prompt: string): string {
 | 
			
		||||
    // 假设原始歌词是以特定分隔符(例如,换行符)分隔的,我们可以将其转换为更易于阅读的格式。
 | 
			
		||||
    // 这里的实现可以根据实际的歌词格式进行调整。
 | 
			
		||||
    // 例如,如果歌词是以连续的文本形式存在,可能需要根据特定的标记(如句号、逗号等)来分割。
 | 
			
		||||
    // 下面的实现假设歌词已经是以换行符分隔的。
 | 
			
		||||
    // Assuming the original lyrics are separated by a specific delimiter (e.g., newline), we can convert it into a more readable format.
 | 
			
		||||
    // The implementation here can be adjusted according to the actual lyrics format.
 | 
			
		||||
    // For example, if the lyrics exist as continuous text, it might be necessary to split them based on specific markers (such as periods, commas, etc.).
 | 
			
		||||
    // The following implementation assumes that the lyrics are already separated by newlines.
 | 
			
		||||
 | 
			
		||||
    // 使用换行符分割歌词,并确保移除空行。
 | 
			
		||||
    // Split the lyrics using newline and ensure to remove empty lines.
 | 
			
		||||
    const lines = prompt.split('\n').filter(line => line.trim() !== '');
 | 
			
		||||
 | 
			
		||||
    // 将处理后的歌词行重新组合成一个字符串,每行之间用换行符分隔。
 | 
			
		||||
    // 可以在这里添加额外的格式化逻辑,如添加特定的标记或者处理特殊的行。
 | 
			
		||||
    // Reassemble the processed lyrics lines into a single string, separated by newlines between each line.
 | 
			
		||||
    // Additional formatting logic can be added here, such as adding specific markers or handling special lines.
 | 
			
		||||
    const formattedLyrics = lines.join('\n');
 | 
			
		||||
 | 
			
		||||
    return formattedLyrics;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Retrieves audio information for the given song IDs.
 | 
			
		||||
   * @param songIds An optional array of song IDs to retrieve information for.
 | 
			
		||||
   * @returns A promise that resolves to an array of AudioInfo objects.
 | 
			
		||||
   */
 | 
			
		||||
  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);
 | 
			
		||||
    logger.info("Get audio status: ", url);
 | 
			
		||||
    const response = await axios.get(url, {
 | 
			
		||||
      headers: {
 | 
			
		||||
        'Authorization': `Bearer ${authToken}`,
 | 
			
		||||
@ -251,7 +278,6 @@ class SunoApi {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
@ -270,7 +296,7 @@ class SunoApi {
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static async get_limit(): Promise<number> {
 | 
			
		||||
  public static async get_credits(): Promise<object> {
 | 
			
		||||
    const authToken = await this.getAuthToken();
 | 
			
		||||
    const response = await axios.get(`${SunoApi.baseUrl}/api/billing/info/`, {
 | 
			
		||||
      headers: {
 | 
			
		||||
@ -278,7 +304,12 @@ class SunoApi {
 | 
			
		||||
        'User-Agent': SunoApi.userAgent,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    return response.data.total_credits_left;
 | 
			
		||||
    return {
 | 
			
		||||
      credits_left: response.data.total_credits_left,
 | 
			
		||||
      period: response.data.period,
 | 
			
		||||
      monthly_limit: response.data.monthly_limit,
 | 
			
		||||
      monthly_usage: response.data.monthly_usage,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user