Douban Collection Export Export Douban user collections (books, movies, music, games) to CSV files. Douban has no official data export; the official API shut down in 2018. What This Skill Can Do - Full export of all book/movie/music/game collections via Frodo API - RSS incremental sync for daily updates (last 10 items) - CSV output with UTF-8 BOM (Excel-compatible), cross-platform (macOS/Windows/Linux) - No login, no cookies, no browser required - Pre-flight user ID validation (fail fast on wrong ID) What This Skill Cannot Do - Cannot export reviews (长评), notes (读书笔记), or broadcasts (广播) - Ca…

, user_id):\n print(f'Error: DOUBAN_USER contains invalid characters: {user_id}')\n sys.exit(1)\n\n output_dir = get_output_dir(user_id)\n os.makedirs(output_dir, exist_ok=True)\n print(f'Douban Export for user: {user_id}')\n print(f'Output directory: {output_dir}\\n')\n\n # Pre-flight: verify user ID is valid before spending time on full export\n if not preflight_check(user_id):\n sys.exit(1)\n\n # Collect data grouped by output file, then write all at the end.\n file_data = {}\n grand_total = 0\n\n for type_name, status, status_cn, outfile in CATEGORIES:\n print(f'=== {status_cn} ({type_name}) ===')\n items = fetch_all_interests(user_id, type_name, status)\n\n if outfile not in file_data:\n file_data[outfile] = []\n\n for item in items:\n file_data[outfile].append(interest_to_row(item, type_name, status_cn))\n\n count = len(items)\n grand_total += count\n if count > 0:\n print(f' Collected: {count}\\n')\n else:\n print(f' (empty)\\n')\n\n time.sleep(CATEGORY_DELAY)\n\n # Write CSV files\n print('--- Writing CSV files ---')\n for filename, rows in file_data.items():\n filepath = os.path.join(output_dir, filename)\n write_csv(filepath, rows)\n print(f' {filename}: {len(rows)} rows')\n\n print(f'\\nDone! {grand_total} total items exported to {output_dir}')\n\n\nif __name__ == '__main__':\n try:\n main()\n except KeyboardInterrupt:\n print('\\n\\nExport interrupted by user.')\n sys.exit(130)\n","content_type":"text/x-python; charset=utf-8","language":"python","size":11056,"content_sha256":"9fe4ce694ab5dc860e2fef64fd4d37a6af288f52a23c8bfc3a01a7925c8bfc9f"},{"filename":"scripts/douban-rss-sync.mjs","content":"#!/usr/bin/env node\n/**\n * Douban RSS → CSV incremental sync\n *\n * Pulls the public RSS feed, parses new entries, appends to CSV files.\n * No login required. Returns only the ~10 most recent items.\n * Best used for daily sync after a full Frodo API export.\n *\n * Usage:\n * DOUBAN_USER=\u003cuser_id> node douban-rss-sync.mjs\n *\n * Environment:\n * DOUBAN_USER (required): Douban user ID\n * DOUBAN_OUTPUT_DIR (optional): Override output directory\n */\n\nimport https from 'node:https';\nimport http from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nlet DOUBAN_USER = process.env.DOUBAN_USER;\nif (!DOUBAN_USER) { console.error('Error: DOUBAN_USER env var is required'); process.exit(1); }\n// Extract ID from full URL if provided (e.g., https://www.douban.com/people/foo/)\nconst urlMatch = DOUBAN_USER.match(/douban\\.com\\/people\\/([A-Za-z0-9._-]+)/);\nif (urlMatch) DOUBAN_USER = urlMatch[1];\nif (!/^[A-Za-z0-9._-]+$/.test(DOUBAN_USER)) { console.error('Error: DOUBAN_USER contains invalid characters'); process.exit(1); }\n\nfunction getDownloadDir() {\n if (process.platform === 'win32') {\n return path.join(process.env.USERPROFILE || os.homedir(), 'Downloads');\n }\n return path.join(os.homedir(), 'Downloads');\n}\n\nconst BASE_DIR = process.env.DOUBAN_OUTPUT_DIR || path.join(getDownloadDir(), 'douban-sync');\nconst DOUBAN_OUTPUT_DIR = path.join(BASE_DIR, DOUBAN_USER);\nconst STATE_FILE = path.join(DOUBAN_OUTPUT_DIR, '.douban-rss-state.json');\nconst RSS_URL = `https://www.douban.com/feed/people/${DOUBAN_USER}/interests`;\n\nconst CATEGORY_MAP = [\n { pattern: /^读过/, file: '书.csv', status: '读过' },\n { pattern: /^(?:在读|最近在读)/, file: '书.csv', status: '在读' },\n { pattern: /^想读/, file: '书.csv', status: '想读' },\n { pattern: /^看过/, file: '影视.csv', status: '看过' },\n { pattern: /^(?:在看|最近在看)/, file: '影视.csv', status: '在看' },\n { pattern: /^想看/, file: '影视.csv', status: '想看' },\n { pattern: /^听过/, file: '音乐.csv', status: '听过' },\n { pattern: /^(?:在听|最近在听)/, file: '音乐.csv', status: '在听' },\n { pattern: /^想听/, file: '音乐.csv', status: '想听' },\n { pattern: /^玩过/, file: '游戏.csv', status: '玩过' },\n { pattern: /^(?:在玩|最近在玩)/, file: '游戏.csv', status: '在玩' },\n { pattern: /^想玩/, file: '游戏.csv', status: '想玩' },\n];\n\nconst CSV_HEADER = '\\ufefftitle,url,date,rating,status,comment\\n';\nconst RATING_MAP = { '力荐': '★★★★★', '推荐': '★★★★', '还行': '★★★', '较差': '★★', '很差': '★' };\n\nfunction httpGet(url, redirects = 0) {\n if (redirects > 5) return Promise.reject(new Error('Too many redirects'));\n return new Promise((resolve, reject) => {\n const mod = url.startsWith('https') ? https : http;\n const req = mod.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' }, timeout: 15000 }, res => {\n if (res.statusCode >= 300 && res.statusCode \u003c 400 && res.headers.location) {\n return httpGet(new URL(res.headers.location, url).href, redirects + 1).then(resolve, reject);\n }\n if (res.statusCode >= 400) return reject(new Error(`HTTP ${res.statusCode} for ${url}`));\n let data = '';\n res.on('data', c => data += c);\n res.on('end', () => resolve(data));\n });\n req.on('error', reject);\n req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });\n });\n}\n\nfunction csvEscape(str) {\n if (!str) return '';\n if (str.includes(',') || str.includes('\"') || str.includes('\\n') || str.includes('\\r')) {\n return '\"' + str.replace(/\"/g, '\"\"') + '\"';\n }\n return str;\n}\n\nfunction parseItems(xml) {\n const items = [];\n const itemRegex = /\u003citem>([\\s\\S]*?)\u003c\\/item>/g;\n let match;\n while ((match = itemRegex.exec(xml)) !== null) {\n const block = match[1];\n const get = tag => {\n const m = block.match(new RegExp(`\u003c${tag}[^>]*>(?:\u003c!\\\\[CDATA\\\\[)?([\\\\s\\\\S]*?)(?:\\\\]\\\\]>)?\u003c\\\\/${tag}>`));\n return m ? m[1].trim() : '';\n };\n const title = get('title');\n const link = get('link');\n const guid = get('guid');\n const pubDate = get('pubDate');\n const desc = get('description');\n const ratingMatch = desc.match(/推荐:\\s*(力荐|推荐|还行|较差|很差)/);\n const rating = ratingMatch ? RATING_MAP[ratingMatch[1]] || '' : '';\n const commentMatch = desc.match(/短评:\\s*([^\u003c]+)/);\n const comment = commentMatch ? commentMatch[1].trim() : '';\n items.push({ title, link, guid, pubDate, rating, comment });\n }\n return items;\n}\n\nfunction loadState() {\n try { return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')); }\n catch { return { lastSyncGuids: [] }; }\n}\n\nfunction saveState(state) { fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2)); }\n\nfunction extractName(title) {\n for (const { pattern } of CATEGORY_MAP) {\n if (pattern.test(title)) return title.replace(pattern, '');\n }\n return title;\n}\n\nfunction isAlreadyInFile(filePath, link) {\n try {\n const content = fs.readFileSync(filePath, 'utf8');\n // Exact URL match as CSV field — avoid false positives from substring matches\n // (e.g., /subject/1234/ matching /subject/12345/)\n return content.includes(',' + link + ',') ||\n content.includes(',' + link + '\\n') ||\n content.includes(',' + link + '\\r');\n } catch { return false; }\n}\n\nfunction formatDate(pubDateStr) {\n try {\n const direct = pubDateStr.match(/(\\d{4}-\\d{2}-\\d{2})/);\n if (direct) return direct[1];\n const d = new Date(pubDateStr);\n const cst = new Date(d.getTime() + 8 * 3600000);\n return cst.toISOString().split('T')[0];\n } catch { return ''; }\n}\n\nfunction ensureCsvFile(filePath) {\n if (!fs.existsSync(filePath)) {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, CSV_HEADER);\n }\n}\n\nfunction appendToCsv(filePath, entry, status) {\n ensureCsvFile(filePath);\n const name = extractName(entry.title);\n const date = formatDate(entry.pubDate);\n const line = [csvEscape(name), csvEscape(entry.link), csvEscape(date),\n csvEscape(entry.rating), csvEscape(status), csvEscape(entry.comment)].join(',') + '\\n';\n fs.appendFileSync(filePath, line);\n}\n\nasync function main() {\n console.log(`Douban RSS Sync for user: ${DOUBAN_USER}`);\n console.log(`Output: ${DOUBAN_OUTPUT_DIR}\\n`);\n console.log('Fetching RSS feed...');\n const xml = await httpGet(RSS_URL);\n const items = parseItems(xml);\n console.log(`Found ${items.length} items in feed`);\n\n const state = loadState();\n const knownGuids = new Set(state.lastSyncGuids || []);\n let newCount = 0;\n\n for (const item of items) {\n if (knownGuids.has(item.guid)) continue;\n const cat = CATEGORY_MAP.find(c => c.pattern.test(item.title));\n if (!cat) { console.log(` Skip (unknown category): ${item.title}`); continue; }\n const filePath = path.join(DOUBAN_OUTPUT_DIR, cat.file);\n if (isAlreadyInFile(filePath, item.link)) { console.log(` Skip (exists): ${item.title}`); continue; }\n console.log(` + ${item.title} → ${cat.file}`);\n appendToCsv(filePath, item, cat.status);\n newCount++;\n }\n\n state.lastSyncGuids = items.map(i => i.guid);\n state.lastSync = new Date().toISOString();\n saveState(state);\n console.log(`\\nDone. ${newCount} new entries added.`);\n}\n\nmain().catch(err => { console.error('Error:', err.message); process.exit(1); });\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":7412,"content_sha256":"eb9023981a4204ae0b42ed3f4436a2bc8588d818ee58b9aa222cafe5c77d6549"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Douban Collection Export","type":"text"}]},{"type":"paragraph","content":[{"text":"Export Douban user collections (books, movies, music, games) to CSV files. Douban has no official data export; the official API shut down in 2018.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"What This Skill Can Do","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Full export of all book/movie/music/game collections via Frodo API","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"RSS incremental sync for daily updates (last ~10 items)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CSV output with UTF-8 BOM (Excel-compatible), cross-platform (macOS/Windows/Linux)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No login, no cookies, no browser required","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pre-flight user ID validation (fail fast on wrong ID)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"What This Skill Cannot Do","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cannot export reviews (长评), notes (读书笔记), or broadcasts (广播)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cannot filter by single category in one run (exports all 4 types together)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cannot access private profiles (returns 0 items silently)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Why Frodo API (Do NOT Use Web Scraping)","type":"text"}]},{"type":"paragraph","content":[{"text":"Douban uses PoW (Proof of Work) challenges on web pages, blocking all HTTP scraping. We tested 7 approaches — only the Frodo API works. ","type":"text"},{"text":"Do NOT attempt","type":"text","marks":[{"type":"strong"}]},{"text":" web scraping, ","type":"text"},{"text":"browser_cookie3","type":"text","marks":[{"type":"code_inline"}]},{"text":"+","type":"text"},{"text":"requests","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"curl","type":"text","marks":[{"type":"code_inline"}]},{"text":" with cookies, or Jina Reader.","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/troubleshooting.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/troubleshooting.md","title":null}}]},{"text":" for the complete failure log of all 7 tested approaches and why each failed.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Security & Privacy","type":"text"}]},{"type":"paragraph","content":[{"text":"The API key and HMAC secret in the script are Douban's ","type":"text"},{"text":"public mobile app credentials","type":"text","marks":[{"type":"strong"}]},{"text":", extracted from the APK. They are shared by all Douban app users and do not identify you. No personal credentials are used or stored. Data is fetched only from ","type":"text"},{"text":"frodo.douban.com","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Full Export (Primary Method)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"DOUBAN_USER=\u003cuser_id> python3 scripts/douban-frodo-export.py","type":"text"}]},{"type":"paragraph","content":[{"text":"Finding the user ID:","type":"text","marks":[{"type":"strong"}]},{"text":" Profile URL ","type":"text"},{"text":"douban.com/people/\u003cID>/","type":"text","marks":[{"type":"code_inline"}]},{"text":" — the ID is after ","type":"text"},{"text":"/people/","type":"text","marks":[{"type":"code_inline"}]},{"text":". If the user provides a full URL, the script auto-extracts the ID.","type":"text"}]},{"type":"paragraph","content":[{"text":"Environment variables:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DOUBAN_USER","type":"text","marks":[{"type":"code_inline"}]},{"text":" (required): Douban user ID (alphanumeric or numeric, or full profile URL)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DOUBAN_OUTPUT_DIR","type":"text","marks":[{"type":"code_inline"}]},{"text":" (optional): Override output directory","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Default output","type":"text","marks":[{"type":"strong"}]},{"text":" (auto-detected per platform):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"macOS: ","type":"text"},{"text":"~/Downloads/douban-sync/\u003cuser_id>/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Windows: ","type":"text"},{"text":"%USERPROFILE%\\Downloads\\douban-sync\\\u003cuser_id>\\","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linux: ","type":"text"},{"text":"~/Downloads/douban-sync/\u003cuser_id>/","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Dependencies:","type":"text","marks":[{"type":"strong"}]},{"text":" Python 3.6+ standard library only (works with ","type":"text"},{"text":"python3","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"uv run","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]},{"type":"paragraph","content":[{"text":"Example console output:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Douban Export for user: your_douban_id\nOutput directory: /Users/you/Downloads/douban-sync/your_douban_id\n\n=== 读过 (book) ===\n Total: 639\n Fetched 0-50 (50/639)\n Fetched 50-100 (100/639)\n ...\n Fetched 597-639 (639/639)\n Collected: 639\n\n=== 在读 (book) ===\n Total: 75\n ...\n\n--- Writing CSV files ---\n 书.csv: 996 rows\n 影视.csv: 238 rows\n 音乐.csv: 0 rows\n 游戏.csv: 0 rows\n\nDone! 1234 total items exported to /Users/you/Downloads/douban-sync/your_douban_id","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"RSS Incremental Sync (Complementary)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"DOUBAN_USER=\u003cuser_id> node scripts/douban-rss-sync.mjs","type":"text"}]},{"type":"paragraph","content":[{"text":"RSS returns only the latest ~10 items (no pagination). Use Full Export first, then RSS for daily updates.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Output Format","type":"text"}]},{"type":"paragraph","content":[{"text":"Four CSV files per user:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Downloads/douban-sync/\u003cuser_id>/\n├── 书.csv (读过 + 在读 + 想读)\n├── 影视.csv (看过 + 在看 + 想看)\n├── 音乐.csv (听过 + 在听 + 想听)\n└── 游戏.csv (玩过 + 在玩 + 想玩)","type":"text"}]},{"type":"paragraph","content":[{"text":"Columns: ","type":"text"},{"text":"title, url, date, rating, status, comment","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"rating","type":"text","marks":[{"type":"code_inline"}]},{"text":": ★ to ★★★★★ (empty if unrated)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"date","type":"text","marks":[{"type":"code_inline"}]},{"text":": YYYY-MM-DD (when the user marked it)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Safe to run multiple times (overwrites with fresh data)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Row counts may be slightly below Douban's displayed count due to delisted items","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask for Douban user ID (from profile URL, or accept full URL)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run: ","type":"text"},{"text":"DOUBAN_USER=\u003cid> python3 scripts/douban-frodo-export.py","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify: row counts in console output should match, check with ","type":"text"},{"text":"wc -l \u003coutput_dir>/*.csv","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"(Optional) Set up RSS sync for daily incremental updates","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/troubleshooting.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/troubleshooting.md","title":null}}]},{"text":" for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Frodo API auth details (HMAC-SHA1 signature computation)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Common errors (code 996 signature error, rate limits, pagination quirks)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Complete failure log of all 7 tested approaches with root causes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Alternative approaches (豆伴 extension, Tampermonkey script, browser console)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"API endpoint reference with response format","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"douban-skill","author":"@skillopedia","source":{"stars":1511,"repo_name":"claude-code-skills","origin_url":"https://github.com/daymade/claude-code-skills/blob/HEAD/douban-skill/SKILL.md","repo_owner":"daymade","body_sha256":"c2e4df8912a276c8dd78302d747f77a1c8d03af2d7779469303cbd8c6ba220e3","cluster_key":"a7e9b955c6f053e0fa76f011881f30d4cd9e581f61a2f6474a1bfab521a55fb0","clean_bundle":{"format":"clean-skill-bundle-v1","source":"daymade/claude-code-skills/douban-skill/SKILL.md","text_bytes":28393,"source_commit":"0633c92cdf1192ebed0b550384ea2a56f0abd520","skills_sh_rank":15204,"attachment_count":3,"text_attachments":3,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"douban-skill/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"integrations-apis","category_label":"Integrations"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"integrations-apis","import_tag":"skills-sh-top20k-2026-11","description":"Export and sync Douban (豆瓣) book/movie/music/game collections to local CSV files via Frodo API. Supports full export (all history) and RSS incremental sync (recent items). Use when the user wants to export Douban reading/watching/listening/gaming history, back up their Douban data, set up incremental sync, or mentions 豆瓣/douban collections. Triggers on: 豆瓣, douban, 读书记录, 观影记录, 书影音, 导出豆瓣.\n"}},"renderedAt":1793666370826}

Douban Collection Export Export Douban user collections (books, movies, music, games) to CSV files. Douban has no official data export; the official API shut down in 2018. What This Skill Can Do - Full export of all book/movie/music/game collections via Frodo API - RSS incremental sync for daily updates (last 10 items) - CSV output with UTF-8 BOM (Excel-compatible), cross-platform (macOS/Windows/Linux) - No login, no cookies, no browser required - Pre-flight user ID validation (fail fast on wrong ID) What This Skill Cannot Do - Cannot export reviews (长评), notes (读书笔记), or broadcasts (广播) - Ca…