{"record":{"id":"cd94c516dc0d20a6","repo":"iawia002/lux","slug":"cookie","errorCode":null,"errorMessage":"请先购买课程，或使用Cookie登录。","messagePattern":"请先购买课程，或使用Cookie登录。","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extractors/geekbang/geekbang.go","lineNumber":113,"sourceCode":"\theanders := map[string]string{\"Origin\": \"https://time.geekbang.org\", \"Content-Type\": \"application/json\", \"Referer\": url}\n\tparams := strings.NewReader(fmt.Sprintf(`{\"id\": %q}`, matches[2]))\n\tres, err := request.Request(http.MethodPost, \"https://time.geekbang.org/serv/v1/article\", params, heanders)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tdefer res.Body.Close() // nolint\n\n\tvar data geekData\n\tif err = json.NewDecoder(res.Body).Decode(&data); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif data.Code < 0 {\n\t\treturn nil, errors.New(string(data.Error))\n\t}\n\n\tif data.Data.VideoID == \"\" && !data.Data.ColumnHadSub {\n\t\treturn nil, errors.New(\"请先购买课程，或使用Cookie登录。\")\n\t}\n\n\t// Get video license token information\n\tparams = strings.NewReader(\"{\\\"source_type\\\":1,\\\"aid\\\":\" + matches[2] + \",\\\"video_id\\\":\\\"\" + data.Data.VideoID + \"\\\"}\")\n\tres, err = request.Request(http.MethodPost, \"https://time.geekbang.org/serv/v3/source_auth/video_play_auth\", params, heanders)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tdefer res.Body.Close() // nolint\n\n\tvar playAuth videoPlayAuth\n\tif err = json.NewDecoder(res.Body).Decode(&playAuth); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif playAuth.Code < 0 {\n\t\treturn nil, errors.New(string(playAuth.Error))\n\t}","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/iawia002/lux/blob/dd00f6d258d80b6684a0b9402d7124e5c18ef42f/extractors/geekbang/geekbang.go#L95-L131","documentation":"The GeekBang article API responded successfully but with an empty VideoID and ColumnHadSub=false: the account behind the supplied cookie has not purchased this paid column, or no cookie reached the request at all. The Chinese message translates to 'Please purchase the course first, or log in with Cookie.'","triggerScenarios":"Running without -c; a cookie belonging to an account without the subscription; free-preview articles whose full video requires purchase.","commonSituations":"Assuming an article link is free; stale exported cookies; team members sharing links but not entitlements.","solutions":["Log in with the purchasing account, export its cookie, and pass it via -c.","Verify in a browser (same account) that the video actually plays.","If you do own the course, re-export the cookie — stale sessions also land here."],"exampleFix":"// before\n$ lux \"https://time.geekbang.org/column/article/123456\"\n// after\n$ lux -c cookie-purchased-account.txt \"https://time.geekbang.org/column/article/123456\"","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isGeekbangPurchaseRequired(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"请先购买课程\")\n}","tryCatchPattern":"Catch and branch on the message: purchase/login prompt means credential or entitlement problems — re-authenticate with the purchasing account and retry. Do not retry with the same cookie; it cannot succeed.","preventionTips":["Confirm the account owns the column before queueing its articles","Separate free-preview and paid URLs in batch files","Re-export cookies whenever entitlements change"],"tags":["geekbang","auth","paid-content","entitlement"],"backgroundTag":null,"analyzedSha":"dd00f6d258d80b6684a0b9402d7124e5c18ef42f","analyzedAt":"2026-08-15T16:57:31.080Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}