{
  "name": "VietlinhAI | AI viết nội dung video ngắn mỗi sáng",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "5ed5343a-6ed7-4748-9bd9-f030b259452a",
      "name": "8H Sáng Mỗi Ngày",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -960,
        496
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn file của bạn --",
          "cachedResultUrl": ""
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn trang tính Auto của bạn --",
          "cachedResultUrl": ""
        },
        "options": {}
      },
      "id": "e932dc57-7ce7-443d-a8b3-26c0f3def343",
      "name": "Đọc Tiêu Đề Đã Làm",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        -688,
        496
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn file của bạn --",
          "cachedResultUrl": ""
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn trang tính Footage của bạn --",
          "cachedResultUrl": ""
        },
        "options": {}
      },
      "id": "2aea7698-52be-4b24-ab07-7d724decb553",
      "name": "Đọc Thư Viện Footage",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        -432,
        496
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn file của bạn --",
          "cachedResultUrl": ""
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn trang tính Nhac của bạn --",
          "cachedResultUrl": ""
        },
        "options": {}
      },
      "id": "db84dbe2-55af-4cc1-bfb6-1fbd570be7a2",
      "name": "Đọc Thư Viện Nhạc",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        -176,
        496
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "Viết bài hôm nay theo ĐÚNG hai chỉ định trong system message: đúng MẢNG CHỦ ĐỀ HÔM NAY (Phần 4) và đúng DẠNG BÀI HÔM NAY (Phần 2). Không tự đổi mảng, không tự đổi dạng.\n\nNhớ đây là chữ chạy trên VIDEO DỌC: noi_dung BẮT BUỘC là danh sách ĐÁNH SỐ 5–6 dòng, mỗi dòng 6–9 từ, dài gần bằng nhau, mỗi dòng là một câu hoàn chỉnh đứng riêng được như châm ngôn. Xuống dòng bằng \\n. Kết bằng 1 dòng CTA dễ làm (không đánh số). TUYỆT ĐỐI không viết văn xuôi rồi bẻ dòng, không kể chuyện, không sến.\n\nTrước khi viết, nghĩ nhanh 2–3 góc trong mảng đó rồi chọn góc SẮC NHẤT, khác rõ so với các tiêu đề đã làm.\n\nYêu cầu chất lượng: mỗi dòng phải nói một điều MỚI, ưu tiên đối chữ hai vế, chữ cụ thể có sức nặng. Che dòng 1 đi mà các dòng sau vẫn nói được điều mới thì đạt; nếu mọi dòng cùng một công thức thì viết lại.\n\nTổng 40–70 từ (trần cứng 70). Chính tả tiếng Việt chuẩn 100%. mood chọn đúng 1 trong 5 giá trị.\n\nChạy phần TỰ KIỂM (Phần 8) trước khi xuất. Chỉ trả về đúng JSON quy định, không markdown, không giải thích.",
        "options": {
          "systemMessage": "=Bạn viết nội dung cho kênh video ngắn (Reels/TikTok/Shorts) '[TÊN KÊNH CỦA BẠN]' — trí tuệ sống thực tế cho người trưởng thành Việt Nam. Giọng: TỈNH TÁO, DỨT KHOÁT, sâu sắc, ấm mà không mềm, sắc mà không gắt. Kênh này CẮT chứ không VUỐT VE. Người xem đọc xong phải thấy tỉnh ra và muốn ghim lại, KHÔNG phải thấy rưng rưng.\n\n════════ PHẦN 1. CẤU TRÚC BẮT BUỘC (QUAN TRỌNG NHẤT) ════════\nĐây là chữ chạy trên VIDEO DỌC, không phải bài đọc. Cấu trúc đã được chứng minh có nhiều view:\n\n  • noi_dung LUÔN là một DANH SÁCH ĐÁNH SỐ 5–6 dòng, xuống dòng bằng \\n.\n  • MỖI DÒNG PHẢI ĐỨNG RIÊNG ĐƯỢC — là một câu hoàn chỉnh, có nghĩa trọn vẹn khi tách khỏi các dòng khác, đọc lên như một câu châm ngôn ghim được.\n  • MỖI DÒNG 6–9 TỪ, các dòng DÀI GẦN BẰNG NHAU → lên video thành khối chữ cân đối, mắt lướt 1 giây một dòng.\n  • Kết bằng 1 dòng CTA riêng (không đánh số).\n  • TUYỆT ĐỐI KHÔNG viết văn xuôi rồi bẻ dòng. Dấu hiệu sai: dòng này phải đọc dòng trước mới hiểu, dòng dài ngắn lộn xộn, có dòng lê thê 12–15 từ, câu bắt đầu bằng 'Mà vì...', 'Không phải vì...', 'Lúc...'. Thấy vậy là hỏng, viết lại.\n\nMẪU CHUẨN CÓ THẬT CỦA KÊNH (bám sát nhịp này):\n  TÂM CÀNG TĨNH, LỘC CÀNG SÂU\n  1. Nước sâu chảy chậm, người khôn nói ít.\n  2. Lộc không tự đến, đức tự chiêu cảm.\n  3. Đừng khoe giàu khi chưa thực sự vững.\n  4. Tiền trong túi mình mới là tiền thật.\n  5. Tránh xa thị phi, tập trung cầu tài.\n  6. Trí tuệ dẫn lối, nhân cách giữ tiền.\n  Để lại chữ \"AN\" nếu bạn chọn bình yên.\n\n════════ PHẦN 2. DẠNG BÀI HÔM NAY — dạng số {{ $('Đọc Tiêu Đề Đã Làm').all().length % 3 }} (BẮT BUỘC, không tự đổi) ════════\n  0. CHÂM NGÔN (như mẫu chuẩn trên): 5–6 dòng, mỗi dòng một lời khuyên/sự thật riêng, ưu tiên ĐỐI CHỮ hai vế ('Nước sâu chảy chậm, người khôn nói ít'). CTA: mời để lại MỘT CHỮ ('Để lại chữ AN nếu...').\n  1. LẬT LẦM TƯỞNG: 5–6 mục, mỗi mục 2 dòng ngắn — dòng trên là điều người ta vẫn tưởng, dòng dưới lật ngược. Mẫu có thật: '1. Người giàu tiêu hoang. / Họ tính từng đồng nhỏ.' — '2. Chăm chỉ sẽ giàu. / Chọn đúng mới giàu.' — '4. Khoe của thì oai. / Giấu của mới bền.' Mỗi mục một cú vặn KHÁC nhau. Tiêu đề dạng 'SÁU LẦM TƯỞNG VỀ...'. CTA: 'Ý nào khiến bạn ngộ ra? Comment số bên dưới.'\n  2. DẤU HIỆU NHẬN BIẾT: 5–6 dòng, mỗi dòng một HÀNH VI quan sát được (không phán xét đạo đức, không nói chuyện cảm xúc chung chung). Ví dụ nhịp đúng: 'Chỉ nhắn tin khi cần bạn giúp.' / 'Hứa chắc trong tiệc, sáng không nhấc máy.' Tiêu đề dạng 'NĂM DẤU HIỆU...', 'KIỂU NGƯỜI NÊN GIỮ KHOẢNG CÁCH'. CTA: mời gọi tên người nên đọc hoặc để lại một chữ.\nCả 3 dạng ĐỀU LÀ DANH SÁCH ĐÁNH SỐ. Không có dạng nào là văn xuôi kể chuyện.\n\n════════ PHẦN 3. CHẤT LƯỢNG TỪNG DÒNG (đây là chỗ quyết định hay/dở) ════════\nCấu trúc đúng chưa đủ — từng dòng phải ĐẮT. Chuẩn:\n  ✔ DÒNG ĐẮT: 'Nước sâu chảy chậm, người khôn nói ít.' / 'Tiền trong túi mình mới là tiền thật.' / 'Lộc không tự đến, đức tự chiêu cảm.' — ngắn, cân, có sức nặng, ghim được, mỗi dòng nói một điều MỚI.\n  ✘ DÒNG RỖNG (căn bệnh, cấm tuyệt đối): 'Thời gian đã cho đi: không lấy lại được.' / 'Lời nói thật lòng: không thu về.' — chuỗi nhãn trừu tượng + phủ định, dòng nào cũng một công thức, đọc dòng 1 là biết hết. Đây KHÔNG phải nội dung, chỉ là khuôn rỗng.\n  ✘ DÒNG LỦNG CỦNG: 'Mà vì họ đã xếp nó vào chỗ khác trong đầu về mình.' — dài, tối nghĩa, phải đọc dòng trước mới hiểu. Cấm.\nPHÉP THỬ BẮT BUỘC: che dòng 1 đi, các dòng sau có còn nói được điều MỚI không? Nếu mọi dòng cùng một công thức → đập đi viết lại. Mỗi dòng phải đứng một mình vẫn có giá trị.\nƯu tiên chữ CỤ THỂ, chữ có sức nặng (tiền, người, lời nói, hành vi) hơn chữ trừu tượng chung chung (yêu thương, trân trọng, ý nghĩa).\n\n════════ PHẦN 4. MẢNG CHỦ ĐỀ HÔM NAY — mảng số {{ $('Đọc Tiêu Đề Đã Làm').all().length % 10 }} (BẮT BUỘC, không tự đổi) ════════\n  0. Cuộc sống & trải nghiệm — thói quen, bài học va vấp, sống chậm, niềm vui ngày thường\n  1. Mối quan hệ & gia đình — cha mẹ, vợ chồng, con cái, bạn bè, tình thân\n  2. Tâm linh & nhân quả — gieo–gặt, đức–lộc, buông bỏ, phúc phần, duyên, biết ơn\n  3. Khẩu nghiệp & lời nói — lời nói ra không thu lại, im lặng đúng lúc, khẩu đức\n  4. Ranh giới & đối nhân xử thế — lòng tin, từ chối, nhìn người qua hành vi, giữ khoảng cách\n  5. Nhân sinh & vòng đời — được–mất theo thời gian, trưởng thành, điều quý giá thật\n  6. Nội tâm & tự trọng — kỷ luật bản thân, làm chủ cảm xúc, vững vàng khi bị đánh giá\n  7. Trí tuệ cổ nhân — dựa trên câu người xưa CÓ THẬT, mở ra tầng nghĩa cho đời sống hôm nay\n  8. Sức khỏe & dưỡng sinh — giấc ngủ, ăn uống, vận động, sức khỏe là tài sản cuối cùng\n  9. Tiền bạc & sự nghiệp — sự thật về tiền, công việc, kỷ luật nghề nghiệp\nRÀO CHẮN: không phải mảng 5 thì KHÔNG lấy tuổi tác/vòng đời làm trục chính; không phải mảng 9 thì KHÔNG lấy tiền bạc làm trục chính. Mảng 1 (gia đình): viết về ranh giới, cách ứng xử, sự thật trong quan hệ — TUYỆT ĐỐI KHÔNG kể chuyện cảm động.\n\n════════ PHẦN 5. CẤM ════════\n  • CẤM SẾN / CỔ TÍCH: mẹ già chờ con, đèn để ngoài hiên, ghế trống bàn ăn, đôi dép ở cửa, áo len đan dở, bà cụ nhân hậu bán hàng, người tốt âm thầm rồi nhiều năm sau được đền đáp, 'người lạ ra tay đúng lúc'. Cấm giọng thương cảm ru ngủ ('lặng lẽ', 'âm thầm', 'cả một đời' dùng để gợi nước mắt).\n  • CẤM KỂ CHUYỆN: không mở bài bằng một cảnh, một nhân vật, một buổi tối. Đây là danh sách châm ngôn, không phải truyện ngắn.\n  • CẤM SÁO RỖNG: 'sống là chính mình', 'yêu thương bản thân', 'trân trọng hiện tại', 'buông bỏ để nhẹ lòng', 'càng cố giữ càng xa', 'thời gian không quay lại', 'im lặng là vàng'.\n  • CẤM chuỗi phủ định song song và chuỗi 'khái niệm : phủ định' lặp một công thức.\n  • QUY TẮC VÀNG: một dòng có thể dán vào bài của bất kỳ ai → dòng đó rỗng → bỏ.\n  • AN TOÀN: không cực đoan, không dạy thao túng. Tâm linh: chiêm nghiệm phổ quát, KHÔNG mê tín/bói toán, không hứa 'chia sẻ sẽ gặp may'. Sức khỏe: nguyên tắc phổ quát, không kê đơn.\n\n════════ PHẦN 6. TIÊU ĐỀ & CTA ════════\n  • tieu_de_1: IN HOA, ngắn, có lực, giữ người xem 3 giây đầu. Ưu tiên dạng đối chữ ('TÂM CÀNG TĨNH, LỘC CÀNG SÂU') hoặc dạng số ('SÁU LẦM TƯỞNG VỀ GIÀU CÓ'). Không giật gân rỗng.\n  • tieu_de_2: bổ nghĩa TRỰC TIẾP cho tieu_de_1, cùng mạch, không lạc ý.\n  • CTA (dòng cuối, không đánh số): phải DỄ LÀM để kéo comment. Xoay các cơ chế: để lại một chữ ('Để lại chữ AN nếu bạn chọn bình yên'), chọn số ('Ý nào khiến bạn ngộ ra? Comment số bên dưới'), gọi tên người nên đọc. Không dùng câu hỏi tu từ rỗng kiểu 'Bạn đang giữ điều gì?'.\n\n════════ PHẦN 7. NGÂN SÁCH & MOOD ════════\n  • Tổng noi_dung 40–70 từ, TRẦN CỨNG 70 (vượt là vỡ khung video dọc). 5–6 dòng × 6–9 từ + CTA là vừa khít.\n  • Chính tả và dấu câu tiếng Việt phải CHUẨN 100%. Mỗi câu đọc lên tự nhiên như lời người thật nói.\n  • mood — chọn ĐÚNG 1 theo cảm xúc chủ đạo: sau_lang (trầm, chiêm nghiệm) | am_ap (tình thân, gần gũi) | tinh_tao (dứt khoát, cảnh tỉnh) | an_nhien (nhẹ nhõm, buông) | hao_hung (chí khí, vươn lên).\n\n════════ PHẦN 8. TỰ KIỂM TRƯỚC KHI XUẤT (bắt buộc) ════════\n  1. CẤU TRÚC: có đúng là danh sách đánh số 5–6 dòng không? Mỗi dòng 6–9 từ, dài gần bằng nhau? Có dòng nào lê thê hoặc phải đọc dòng trước mới hiểu không? → sai thì viết lại.\n  2. MỖI DÒNG có đứng riêng được như một câu châm ngôn không? Có nói điều MỚI so với các dòng khác không?\n  3. Có dòng nào rỗng (khái niệm + phủ định), sáo mòn, hoặc dán được vào bài bất kỳ ai không?\n  4. Có mùi SẾN hay kể chuyện không? Có → đập đi viết lại.\n  5. Đúng mảng hôm nay? Đúng dạng hôm nay? Tổng ≤70 từ? Chính tả chuẩn? CTA dễ làm?\n\n════════ PHẦN 9. TRÁNH TRÙNG ════════\nTiêu đề đã làm — góc hôm nay phải khác rõ ràng:\n{{ $('Đọc Tiêu Đề Đã Làm').all().map(i => '- ' + (i.json['Tiêu đề 1'] || '') + ' / ' + (i.json['Tiêu đề 2'] || '')).join('\\n') }}\n\n════════ ĐẦU RA — CHỈ TRẢ VỀ ĐÚNG JSON, không markdown, không giải thích ════════\n{\n  \"tieu_de_1\": \"tiêu đề chính, IN HOA, ngắn, có lực\",\n  \"tieu_de_2\": \"câu bổ nghĩa trực tiếp cho tieu_de_1\",\n  \"noi_dung\": \"1. Dòng châm ngôn thứ nhất.\\n2. Dòng thứ hai.\\n3. Dòng thứ ba.\\n4. Dòng thứ tư.\\n5. Dòng thứ năm.\\nCTA một dòng, không đánh số.\",\n  \"mood\": \"sau_lang | am_ap | tinh_tao | an_nhien | hao_hung\"\n}"
        }
      },
      "id": "fa083401-e267-4ebb-af59-3721cf791aed",
      "name": "AI Agent - Tạo Nội Dung",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.7,
      "position": [
        80,
        496
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "jsCode": "function extractFirstJsonObject(text) {\n  const start = text.indexOf('{');\n  if (start === -1) {\n    throw new Error('Không tìm thấy JSON object trong output. Raw: ' + text.substring(0, 200));\n  }\n  let depth = 0;\n  for (let i = start; i < text.length; i++) {\n    if (text[i] === '{') depth++;\n    else if (text[i] === '}') {\n      depth--;\n      if (depth === 0) return text.slice(start, i + 1);\n    }\n  }\n  throw new Error('JSON object không đóng đúng (thiếu dấu }). Raw: ' + text.substring(0, 200));\n}\n\nlet raw = $json.output || $json.text || '';\n\nlet parsed;\ntry {\n  const jsonStr = extractFirstJsonObject(raw);\n  parsed = JSON.parse(jsonStr);\n} catch (e) {\n  throw new Error('Không parse được nội dung JSON: ' + e.message);\n}\n\n// Mood: AI được yêu cầu trả 1 trong 5 giá trị chuẩn.\n// Nếu trả giá trị lạ vẫn cho đi qua — node \"Chọn Footage & Nhạc\" sẽ tự chuẩn hóa/fallback.\nconst mood = (parsed.mood || '').toString().trim().toLowerCase();\n\nreturn [{\n  json: {\n    'Tiêu đề 1': parsed.tieu_de_1,\n    'Tiêu đề 2': parsed.tieu_de_2,\n    'Nội dung': parsed.noi_dung,\n    'Mood': mood,\n  }\n}];"
      },
      "id": "591b924a-f620-4681-a8bf-c351d7bb9d16",
      "name": "Parse Nội Dung AI",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        704,
        496
      ]
    },
    {
      "parameters": {
        "jsCode": "// ============================================================\n// BẢN 2.0 — CHỌN FOOTAGE (LRU) & NHẠC (MOOD MATCHING + LRU)\n// Logic 3 tầng cho nhạc:\n//   Tầng 1: lọc đúng mood chính của bài -> LRU trong nhóm đó\n//   Tầng 2: mood chính hết bài -> nới ra pool mood được phép -> LRU\n//   Tầng 3: vẫn rỗng (hoặc mood lạ/trống) -> LRU toàn thư viện\n// Nguyên tắc: ưu tiên đúng cảm xúc, nhưng KHÔNG BAO GIỜ để workflow đứng hình\n// ============================================================\n\nconst footageLibrary = $('Đọc Thư Viện Footage').all().map(item => item.json);\nconst musicLibrary = $('Đọc Thư Viện Nhạc').all().map(item => item.json);\nconst usedHistory = $('Đọc Tiêu Đề Đã Làm').all().map(item => item.json);\n\n// ---------- 1. CHUẨN HÓA MOOD ----------\n// Đưa mọi biến thể (có dấu, viết hoa, tag cũ trong sheet) về 5 mood chuẩn.\n// Tag cũ trong tab Nhac (do_thi_hien_dai, manh_me_quyet_doan, dong_luc, soi_dong...)\n// được map sẵn ở đây nên KHÔNG bắt buộc phải dọn sheet ngay, nhưng nên dọn dần.\nconst MOOD_ALIAS = {\n  // sau_lang\n  'sau_lang': 'sau_lang', 'sâu lắng': 'sau_lang', 'sau lang': 'sau_lang',\n  'chiem_nghiem': 'sau_lang', 'chiêm nghiệm': 'sau_lang',\n  'tham_thuy': 'sau_lang', 'thâm thúy': 'sau_lang', 'tram_lang': 'sau_lang',\n  // am_ap\n  'am_ap': 'am_ap', 'ấm áp': 'am_ap', 'am ap': 'am_ap', 'gan_gui': 'am_ap',\n  // tinh_tao\n  'tinh_tao': 'tinh_tao', 'tỉnh táo': 'tinh_tao', 'tinh tao': 'tinh_tao',\n  'manh_me_quyet_doan': 'tinh_tao', 'do_thi_hien_dai': 'tinh_tao', 'dut_khoat': 'tinh_tao',\n  // an_nhien\n  'an_nhien': 'an_nhien', 'an nhiên': 'an_nhien', 'an nhien': 'an_nhien',\n  'binh_yen': 'an_nhien', 'thu_thai': 'an_nhien', 'healing': 'an_nhien',\n  // hao_hung\n  'hao_hung': 'hao_hung', 'hào hùng': 'hao_hung', 'hao hung': 'hao_hung',\n  'hào hứng': 'hao_hung', 'dong_luc': 'hao_hung', 'soi_dong': 'hao_hung',\n  'truyen_cam_hung': 'hao_hung', 'epic': 'hao_hung',\n};\n\nfunction normalizeMood(raw) {\n  if (!raw) return null;\n  const key = String(raw).trim().toLowerCase();\n  return MOOD_ALIAS[key] || null; // mood lạ -> null -> rơi xuống tầng 3, không lỗi\n}\n\n// ---------- 2. POOL NHẠC ĐƯỢC PHÉP CHO TỪNG MOOD NỘI DUNG ----------\n// Many-to-many, KHÔNG bó cứng: mood \"kén\" thì pool hẹp, mood \"dễ tính\" pool rộng.\n// Muốn nới/siết mood nào, chỉ cần sửa mảng tương ứng ở đây.\nconst MOOD_POOLS = {\n  sau_lang: ['sau_lang', 'an_nhien'],\n  am_ap:    ['am_ap', 'an_nhien'],\n  tinh_tao: ['tinh_tao', 'hao_hung'],\n  an_nhien: ['an_nhien', 'sau_lang', 'am_ap'],\n  hao_hung: ['hao_hung', 'tinh_tao'],\n};\n\n// ---------- 3. LRU: chọn bài ít dùng nhất, random trong nhóm ít dùng nhất ----------\nfunction pickLeastUsed(pool, linkField, historyField) {\n  if (!pool || pool.length === 0) return null;\n  const usageCount = {};\n  pool.forEach(row => { usageCount[row[linkField]] = 0; });\n  usedHistory.forEach(row => {\n    const link = row[historyField];\n    if (link && usageCount.hasOwnProperty(link)) usageCount[link] += 1;\n  });\n  const minCount = Math.min(...pool.map(row => usageCount[row[linkField]] || 0));\n  const leastUsedPool = pool.filter(row => (usageCount[row[linkField]] || 0) === minCount);\n  return leastUsedPool[Math.floor(Math.random() * leastUsedPool.length)];\n}\n\n// ---------- 4. CHỌN NHẠC THEO 3 TẦNG ----------\nconst contentMood = normalizeMood($json.Mood);\nlet musicPool = [];\nlet moodStrategy = 'toan_bo'; // để debug: mood_chinh | mood_mo_rong | toan_bo\n\nif (contentMood) {\n  // Tầng 1: đúng mood chính\n  const primaryPool = musicLibrary.filter(row => normalizeMood(row['Mood']) === contentMood);\n  if (primaryPool.length > 0) {\n    musicPool = primaryPool;\n    moodStrategy = 'mood_chinh';\n  } else {\n    // Tầng 2: nới ra các mood được phép\n    const allowedMoods = MOOD_POOLS[contentMood] || [];\n    const extendedPool = musicLibrary.filter(row => allowedMoods.includes(normalizeMood(row['Mood'])));\n    if (extendedPool.length > 0) {\n      musicPool = extendedPool;\n      moodStrategy = 'mood_mo_rong';\n    }\n  }\n}\n\n// Tầng 3: fallback cuối — toàn thư viện (mood trống/lạ, hoặc pool rỗng)\nif (musicPool.length === 0) {\n  musicPool = musicLibrary;\n  moodStrategy = 'toan_bo';\n}\n\nconst chosenMusic = pickLeastUsed(musicPool, 'Link Music', 'Link Nhạc');\n\n// ---------- 5. FOOTAGE: giữ nguyên LRU toàn thư viện ----------\nconst chosenFootage = pickLeastUsed(footageLibrary, 'Link Video Footage', 'Link Video Footage');\n\nconst existingCount = $('Đọc Tiêu Đề Đã Làm').all().length;\n\nreturn [{\n  json: {\n    ...$json,\n    'ID': existingCount + 1,\n    'Link Video Footage': chosenFootage ? chosenFootage['Link Video Footage'] : '',\n    'Link Nhạc': chosenMusic ? chosenMusic['Link Music'] : '',\n    'Mood': contentMood || ($json.Mood || ''),\n    'Mood Strategy': moodStrategy, // chỉ để debug trong execution log, không ghi vào sheet\n    'Status': 'Ready to Post',\n  }\n}];"
      },
      "id": "410bc6e9-add5-4639-8391-bed7fe913392",
      "name": "Chọn Footage & Nhạc",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        928,
        496
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn file của bạn --",
          "cachedResultUrl": ""
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn trang tính của bạn --",
          "cachedResultUrl": ""
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "ID": "={{ $json.ID }}",
            "Tiêu đề 1": "={{ $json['Tiêu đề 1'] }}",
            "Tiêu đề 2": "={{ $json['Tiêu đề 2'] }}",
            "Nội dung": "={{ $json['Nội dung'] }}",
            "Link Video Footage": "={{ $json['Link Video Footage'] }}",
            "Link Nhạc": "={{ $json['Link Nhạc'] }}",
            "Mood": "={{ $json.Mood }}",
            "Status": "Ready to Product"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "ID",
              "displayName": "ID",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Tiêu đề 1",
              "displayName": "Tiêu đề 1",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Tiêu đề 2",
              "displayName": "Tiêu đề 2",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Nội dung",
              "displayName": "Nội dung",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Link Video Footage",
              "displayName": "Link Video Footage",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Link Nhạc",
              "displayName": "Link Nhạc",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Mood",
              "displayName": "Mood",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "displayName": "Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "9519618c-199c-4916-a2af-b568dc88e92c",
      "name": "Thêm Nội Dung Mới",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1184,
        496
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn model của bạn --"
        },
        "responsesApiEnabled": "={{ false }}",
        "builtInTools": {},
        "options": {
          "maxTokens": 2000,
          "temperature": 0.8
        }
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        272,
        832
      ],
      "id": "b8fb53aa-db3f-4aeb-8741-4cae2cb89f43",
      "name": "Chat Model"
    },
    {
      "parameters": {
        "content": "## 🧠 v2.5 — LOGIC NỘI DUNG (reels-first)\n\n**CẤU TRÚC BẤT BIẾN** (đã chứng minh có view):\nDanh sách ĐÁNH SỐ 5–6 dòng · mỗi dòng 6–9 từ\nMỗi dòng đứng riêng được như châm ngôn\nCác dòng dài gần bằng nhau → khối chữ cân trên video\nKết bằng 1 CTA dễ làm (để lại 1 chữ / comment số)\n\n**Xoay 10 mảng chủ đề** = số bài đã có % 10\n0 Cuộc sống · 1 Gia đình · 2 Tâm linh · 3 Khẩu nghiệp · 4 Ranh giới\n5 Nhân sinh · 6 Nội tâm · 7 Cổ nhân · 8 Sức khỏe · 9 Tiền bạc\n\n**Xoay 3 dạng bài** = số bài đã có % 3\n0 Châm ngôn · 1 Lật lầm tưởng · 2 Dấu hiệu nhận biết\n(cả 3 đều là danh sách đánh số)\n\n**Chất lượng cấp DÒNG** — chỗ quyết định hay/dở:\n✔ 'Nước sâu chảy chậm, người khôn nói ít.'\n✘ 'Thời gian đã cho đi: không lấy lại được.' (rỗng)\n✘ 'Mà vì họ đã xếp nó vào chỗ khác...' (lủng củng)\nPhép thử: che dòng 1, các dòng sau còn nói điều mới không?\n\n**CẤM:** sến/cổ tích · kể chuyện · văn xuôi bẻ dòng\n**Ngân sách:** 40–70 từ (trần cứng 70)",
        "height": 516,
        "width": 380
      },
      "id": "ab709b8d-4487-4f4d-8176-b6013981083d",
      "name": "Sticky Note - Logic Nội Dung",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -80
      ]
    },
    {
      "parameters": {
        "content": "## 🎵 BẢN 2.0 — GHÉP NHẠC THEO MOOD (3 TẦNG)\n\n**Tầng 1:** lọc nhạc ĐÚNG mood chính → LRU\n**Tầng 2:** mood chính hết bài → nới ra pool mood được phép (MOOD_POOLS) → LRU\n**Tầng 3:** vẫn rỗng / mood lạ / mood trống → LRU toàn thư viện\n\n⚙️ Muốn nới/siết pool mood nào: sửa MOOD_POOLS trong code.\n⚙️ Tag mood cũ trong tab Nhac (dong_luc, soi_dong...) đã được map tự động qua MOOD_ALIAS — nên dọn dần về 5 mood chuẩn.\n\n📋 Tab Nhac cần cột **Mood** điền 1 trong 5 giá trị:\nsau_lang · am_ap · tinh_tao · an_nhien · hao_hung",
        "height": 492,
        "width": 400
      },
      "id": "fac6c6dc-79b8-40a2-999e-b161a91b3614",
      "name": "Sticky Note - Ghép Nhạc",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        512,
        -80
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Bạn là biên tập viên tiếng Việt khó tính của kênh video ngắn '[TÊN KÊNH CỦA BẠN]'. Dưới đây là JSON chứa bài viết. Nhiệm vụ:\n1. Sửa mọi lỗi chính tả, lỗi dấu câu tiếng Việt.\n2. GIỮ NGUYÊN cấu trúc danh sách đánh số và cách xuống dòng. Không gộp dòng, không bỏ số thứ tự.\n3. Nếu có DÒNG LỦNG CỦNG (quá dài, tối nghĩa, phải đọc dòng trước mới hiểu), viết lại cho ngắn gọn 6–9 từ, đứng riêng vẫn có nghĩa trọn vẹn — giữ đúng ý gốc.\n4. Nếu có DÒNG RỖNG (khái niệm trừu tượng + phủ định, sáo mòn kiểu 'càng cố giữ càng xa', 'thời gian không quay lại') hoặc giọng SẾN (thương cảm, ru ngủ), viết lại cho cụ thể và tỉnh táo hơn, bám ý sẵn có, không bịa tình tiết mới.\n5. Cân lại độ dài các dòng cho gần bằng nhau để chữ lên video cân đối.\nKHÔNG đổi chủ đề, KHÔNG thêm ý mới, KHÔNG bỏ ý, KHÔNG đổi giá trị mood. Tổng không vượt 70 từ.\nTrả về DUY NHẤT JSON đúng cấu trúc gốc (tieu_de_1, tieu_de_2, noi_dung, mood), không markdown, không giải thích.\n\nJSON cần biên tập:\n{{ $json.output }}"
      },
      "id": "9834ec99-0826-4c30-88df-c3b248de4f1d",
      "name": "QC Biên Tập",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.5,
      "position": [
        400,
        496
      ]
    }
  ],
  "pinData": {},
  "connections": {
    "8H Sáng Mỗi Ngày": {
      "main": [
        [
          {
            "node": "Đọc Tiêu Đề Đã Làm",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Đọc Tiêu Đề Đã Làm": {
      "main": [
        [
          {
            "node": "Đọc Thư Viện Footage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Đọc Thư Viện Footage": {
      "main": [
        [
          {
            "node": "Đọc Thư Viện Nhạc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Đọc Thư Viện Nhạc": {
      "main": [
        [
          {
            "node": "AI Agent - Tạo Nội Dung",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent - Tạo Nội Dung": {
      "main": [
        [
          {
            "node": "QC Biên Tập",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Nội Dung AI": {
      "main": [
        [
          {
            "node": "Chọn Footage & Nhạc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chọn Footage & Nhạc": {
      "main": [
        [
          {
            "node": "Thêm Nội Dung Mới",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "QC Biên Tập": {
      "main": [
        [
          {
            "node": "Parse Nội Dung AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent - Tạo Nội Dung",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "QC Biên Tập",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "nodeGroups": [],
  "tags": []
}