{"record":{"id":"779a7543a7403d71","repo":"iawia002/lux","slug":"unable-to-get-audio-id","errorCode":null,"errorMessage":"unable to get audio ID","messagePattern":"unable to get audio ID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extractors/ximalaya/ximalaya.go","lineNumber":41,"sourceCode":"}\n\n// Extract is the main function to extract the data.\nfunc (e *extractor) Extract(url string, option extractors.Options) ([]*extractors.Data, error) {\n\thtml, err := request.Get(url, url, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\t// get the title\n\tdoc, err := parser.GetDoc(html)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\ttitle := parser.Title(doc)\n\n\titemIds := utils.MatchOneOf(url, `/sound/(\\d+)`)\n\tif len(itemIds) == 0 {\n\t\treturn nil, errors.New(\"unable to get audio ID\")\n\t}\n\tif itemIds == nil || len(itemIds) < 2 {\n\t\treturn nil, errors.WithStack(extractors.ErrURLParseFailed)\n\t}\n\titemId := itemIds[len(itemIds)-1]\n\n\tjsonData, err := request.Get(\"https://www.ximalaya.com/revision/play/v1/audio?id=\"+itemId+\"&ptype=1\", url, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tvar ximalaya ximalayaData\n\tif err = json.Unmarshal([]byte(jsonData), &ximalaya); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\trealURL := ximalaya.Data.Src\n\turlData := make([]*extractors.Part, 0)\n\ttotalSize, err := request.Size(realURL, url)","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/iawia002/lux/blob/dd00f6d258d80b6684a0b9402d7124e5c18ef42f/extractors/ximalaya/ximalaya.go#L23-L59","documentation":"The Ximalaya extractor requires the URL itself to contain a /sound/<digits> segment (a single audio track). It runs utils.MatchOneOf(url, `/sound/(\\d+)`); when there is no match, len(itemIds) == 0 and 'unable to get audio ID' is returned. The numeric sound id is mandatory because the playback API URL is built from it.","triggerScenarios":"Passing an album/playlist URL (https://www.ximalaya.com/album/12345678) instead of a single track, or share/search URLs that do not carry the /sound/ path segment. Note the code checks len == 0 before the nil check, so both nil and empty results land here.","commonSituations":"Users copy the album page link from the browser address bar; scripts enumerate album URLs expecting per-track extraction; mobile share URLs use a different path scheme.","solutions":["Open the album in a browser and copy the URL of an individual track (contains /sound/123456)","If you have album URLs, first expand them to their track URLs (album API or page parsing) before calling Extract","Normalize share links to the canonical www.ximalaya.com/sound/<id> form","Validate the /sound/\\d+ pattern on the caller side before invoking Extract"],"exampleFix":"// before\n_, err := ximalaya.Extract(\"https://www.ximalaya.com/album/12345678\", opts) // -> unable to get audio ID\n\n// after\n_, err := ximalaya.Extract(\"https://www.ximalaya.com/sound/456033392\", opts) // single track URL","handlingStrategy":"validation","validationCode":"var ximalayaSoundRe = regexp.MustCompile(`^https?://www\\.ximalaya\\.com/sound/\\d+`)\n\nfunc isXimalayaTrackURL(raw string) bool {\n    return ximalayaSoundRe.MatchString(raw)\n}","typeGuard":null,"tryCatchPattern":"if !isXimalayaTrackURL(url) {\n    return fmt.Errorf(\"expected a ximalaya /sound/<id> track URL, got %s\", url)\n}\n_, err := ximalaya.Extract(url, opts)","preventionTips":["Expand album URLs to their track URLs before extraction","Canonicalize share links to www.ximalaya.com/sound/<id>","URL-shape validation is cheap and converts runtime errors into early skips"],"tags":["ximalaya","url-validation","album-vs-track"],"backgroundTag":null,"analyzedSha":"dd00f6d258d80b6684a0b9402d7124e5c18ef42f","analyzedAt":"2026-08-15T16:57:31.080Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}