This feed does not validate.
... 025/06/banner-社群經營.jpeg" medium="image"></media:content>
^
... 31-下午2.46.02-scaled.png" medium="image"></media:content>
^
In addition, interoperability with the widest range of feed readers could be improved by implementing the following recommendations.
line 31, column 0: (11 occurrences) [help]
<site xmlns="com-wordpress:feed-additions:1">33236058</site> <item>
line 49, column 0: (10 occurrences) [help]
<description><![CDATA[六月快結束了,接下來是暑假。今天在想,六月發生什麼值得記錄的事呢? 除了從Pocket 跳船到 K ...
line 98, column 0: (26 occurrences) [help]
<div class="wp-block-image">
<div class="wp-block-image">
line 98, column 0: (41 occurrences) [help]
<div class="wp-block-image">
line 98, column 0: (26 occurrences) [help]
<div class="wp-block-image">
line 621, column 0: (29 occurrences) [help]
line 828, column 0: (8 occurrences) [help]
line 828, column 0: (64 occurrences) [help]
line 828, column 0: (12 occurrences) [help]
line 828, column 0: (23 occurrences) [help]
line 828, column 0: (6 occurrences) [help]
line 1798, column 0: (23 occurrences) [help]
<div class="wp-block-image">
line 2597, column 0: (2 occurrences) [help]
line 3966, column 0: (15 occurrences) [help]
line 3966, column 0: (21 occurrences) [help]
line 3966, column 0: (15 occurrences) [help]
line 3966, column 0: (15 occurrences) [help]
line 3966, column 0: (15 occurrences) [help]
line 3966, column 0: (15 occurrences) [help]
line 4311, column 0: (3 occurrences) [help]
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-emb ...
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
<title>終極邊疆 BLOG</title>
<atom:link href="https://blog.serv.idv.tw/feed/" rel="self" type="application/rss+xml" />
<link>https://blog.serv.idv.tw</link>
<description>在這個時代, blogging 就像是深呼吸一樣。</description>
<lastBuildDate>Sun, 29 Jun 2025 12:14:33 +0000</lastBuildDate>
<language>zh-TW</language>
<sy:updatePeriod>
hourly </sy:updatePeriod>
<sy:updateFrequency>
1 </sy:updateFrequency>
<image>
<url>https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2005/04/cropped-Me-at-Flickr_400.jpg?fit=32%2C32&ssl=1</url>
<title>終極邊疆 BLOG</title>
<link>https://blog.serv.idv.tw</link>
<width>32</width>
<height>32</height>
</image>
<site xmlns="com-wordpress:feed-additions:1">33236058</site> <item>
<title>六月做什麼</title>
<link>https://blog.serv.idv.tw/2025/06/june-playing-with-kids/</link>
<comments>https://blog.serv.idv.tw/2025/06/june-playing-with-kids/#respond</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Mon, 30 Jun 2025 00:05:00 +0000</pubDate>
<category><![CDATA[GTD]]></category>
<category><![CDATA[小平安計畫]]></category>
<category><![CDATA[小幸福計畫]]></category>
<category><![CDATA[小平安]]></category>
<category><![CDATA[小幸福]]></category>
<category><![CDATA[會考]]></category>
<category><![CDATA[注意力]]></category>
<category><![CDATA[社群媒體]]></category>
<category><![CDATA[遊戲]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8300</guid>
<description><![CDATA[六月快結束了,接下來是暑假。今天在想,六月發生什麼值得記錄的事呢? 除了從Pocket 跳船到 Keep,學了 ... <a title="六月做什麼" class="read-more" href="https://blog.serv.idv.tw/2025/06/june-playing-with-kids/" aria-label="Read more about 六月做什麼">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>六月快結束了,接下來是暑假。<br>今天在想,六月發生什麼值得記錄的事呢?</p>
<p>除了<a href="https://blog.serv.idv.tw/2025/06/from-pocket-to-karakeep/">從Pocket 跳船到 Keep</a>,<a href="https://blog.serv.idv.tw/2025/06/n8n-workflow-karakeep/">學了 n8n</a> ,<a href="https://blog.serv.idv.tw/2025/06/colab-whisperx-transcript-revised-50619/">重改了 Colab+ WhisperX 腳本</a>之外,生活裡,印象較深的就是跟兩個孩子一起玩遊戲和課業吧。</p>
<p>又要玩又要顧課業,還真的是 study hard, play hard.</p>
<span id="more-8300"></span>
<p></p>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p></p>
<h2 class="wp-block-heading">與小平安的遊戲時光</h2>
<p>過去兩年來,我一直有在「陪」兩個孩子玩手機遊戲: <a href="https://www.pokemonunite.jp/tc/">Pokemon Unite</a>。我也已經忘記動機是什麼了,一開始是創了一個角色,陪小幸福玩,她玩遠攻輸出,我就玩坦。當時小平安還不到10歲,不能玩,只能在一旁看。然後當小平安10歲之後,很快他創了角色,走近戰風,我們三個就常在週末假日約時間一起玩。</p>
<p>他們兩位在電玩上比我有天份多了,不管是走位,操作,技能和道具配置,或是戰術策略,面對不同敵人的處理方式,都比我純熟多了。很快地,我就從坦,變成被他們carry 的角色。等級或排位,這兩位也是遠遠地超越我。我不意外,畢竟我一開始只是「陪玩」的心態, 但這樣的陪伴,卻帶給我無價的親子時光和滿滿的歡笑… 好啦,還是有衝突的時候,尤其是當我們這隊輸的時候,大家心情不好,開始檢討隊友的時候。此時我最常「被檢討」…畢竟我是三人裡最弱的角色。</p>
<p>六月,我手上有一個遊戲想玩很久了,所以邀請小平安一起玩《<a href="https://zh.wikipedia.org/zh-tw/%E5%8F%8C%E4%BA%BA%E6%88%90%E8%A1%8C">雙人成行</a>》(It Takes Two)。這款遊戲是一款合作冒險遊戲,於2021年由霧影工作室開發,二代雙影奇境最近才上市。玩家需要兩人協作完成各種解謎和動作挑戰。我看小平安平常蠻喜歡玩跑酷類的遊戲,之前我玩「A Story about My Uncle」時他也常湊過來看,甚至在一些關卡接手破關。我相信他對這方面的遊戲是有興趣的,雖然問他時,他總是會說「啊…我可能會3D 暈」,但是把控制器交到他手上後,身體卻又很誠實地玩了起來。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" fetchpriority="high" decoding="async" width="1024" height="576" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/it-takes-two-%E9%9B%99%E4%BA%BA%E6%88%90%E8%A1%8C-%E9%81%8A%E6%88%B2.jpg?resize=1024%2C576&ssl=1" alt="" class="wp-image-8302" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/it-takes-two-%E9%9B%99%E4%BA%BA%E6%88%90%E8%A1%8C-%E9%81%8A%E6%88%B2.jpg?resize=1024%2C576&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/it-takes-two-%E9%9B%99%E4%BA%BA%E6%88%90%E8%A1%8C-%E9%81%8A%E6%88%B2.jpg?resize=400%2C225&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/it-takes-two-%E9%9B%99%E4%BA%BA%E6%88%90%E8%A1%8C-%E9%81%8A%E6%88%B2.jpg?resize=1536%2C864&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/it-takes-two-%E9%9B%99%E4%BA%BA%E6%88%90%E8%A1%8C-%E9%81%8A%E6%88%B2.jpg?w=2000&ssl=1 2000w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>
<p>雙人成行的劇情圍繞一對面臨婚姻危機的夫妻被魔法變成玩偶後的冒險故事。開始玩之後,小平安看起來對主創劇情的推進和夫妻逐一面對婚姻的課題並沒有興趣,但對於各個關卡對人物的不同玩法操作他卻深深著迷。他最喜歡在城堡地牢化身魔法師的那段,看起來是以後可以揪去玩 Diablo 的玩伴。裡頭有許多場景讓他跑酷,我還在探索主線要如何推進時,他已經四處遊山玩水,把遊戲裡的空氣牆和死角都探了個遍。我在玩遊戲時注重情節的走向,想知道主線的下一段會怎麼走,該如何觸發;他則是不厭煩地玩著目前這個場景可以爬、可以跳、可以互動的所有設置 — 包括哪裡/怎樣才會死,死了會在哪裡重生…</p>
<p>跟他一起玩,真的讓我體驗到遊戲心態的不同。</p>
<p>而不只是我學到東西,他也是。因為遊戲對於死亡是沒有什麼penalty 的 (會不斷重生),再加上之前玩 minecraft 的習慣。我發現他走路不好好的走,總是跳跳跳,我走到懸崖邊會停下來看看路,想一想接下來怎麼一路跳/盪過去;他則是用瞬間的判斷力和走位先飛出去,然後在空中接續下一步。好處是行雲流水但常常捏一把冷汗,缺點就是常常跳空、煞不住、重生後沒想清楚就衝出去…然後又掛了。</p>
<p>幾次之後,就換我碎碎念要他先停下來冷靜一下… 這種場景在拿到他考卷,問他這題為什麼錯的時候,好像也會發生。有時是題目看太快,看前幾個字就寫答案了;有時是檢查的時候跳過這一題… 他在遊戲中即時判斷的能力,有助於在考試中快速答榮,但也可能導致缺乏耐心檢查答案。這部份我就只在遊戲中提醒他,希望他有機會可以自己思考。</p>
<p></p>
<p>但在某些點上,他卻也有他的執著和堅持。</p>
<p>玩遊戲跟考試,應該也是有些相似的地方…</p>
<p>總之,六月份的雙人成行,我跟小平安都玩的很開心。全破之後,現在開始解成就跟解鎖全部的小遊戲,順便回去複習喜愛的關卡。照這個態勢,應該有機會推坑他一起玩雙影奇境。</p>
<p>至於小幸福…已經喊+1 想要玩了。但…她真的有時間嗎?</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p></p>
<h2 class="wp-block-heading">小幸福開始面對一年後的會考</h2>
<p>小幸福已經要升國三了。六月份老師開始安排學長姐回校講座,傳授國三會考的準備方式以及高中以後的生涯規劃。再加上國三學長姐會考的成績陸續出爐,家長們開始討論、比較。我自己體感「會考」這個跟過去聯考很像的大考,壓力慢慢罩在小幸福的頭上。</p>
<p>不過就我的觀察,壓力在罩在孩子的頭上之前,是先罩在家長們的頭上的。身邊家長們的焦慮情緒逐漸升溫,但孩子還不一定有。言談中她心思還是在玩手遊、玩社群,讀書的部份就交給學校和補習班。</p>
<p>這當然跟「家長的期待」有一段…<strong>很…大…的gap</strong>。</p>
<p>家長對會考的期待往往帶有強烈的目標導向。希望孩子能考進理想的高中,為未來的競爭鋪路。這種期待不僅來自對孩子未來的關心,也受到社會氛圍的影響。六月,當學長姐的講座和會考成績的公布接踵而來,身邊的家長們開始頻繁交流,討論孩子的進度、總複習補習班,以及模擬考的日期和考試範圍。這種氛圍讓我感到壓力,進而將期望轉嫁到孩子身上。導致我自己在這個月,時常思考如何讓小幸福認真面對一年後的會考,同時確保她不會因為壓力過大而失去學習的動力。</p>
<p>好啦,跟其他家長比起來,我可能還算是後知後覺的…</p>
<p></p>
<p>小幸福的反應,與我的期待形成鮮明對比。對她來說,會考的壓力還很遙遠,手遊和社群媒體才是當下更吸引她的焦點。對她而言,讀書就是去學校和補習班時會發生的事。回家把學校的功課寫完後,就想放鬆一下。這種「唸好眼前的進度,然後玩樂休息」的抗拒心態,與家長「規劃好,必須全力以赴」的期待產生衝突。這種衝突並不罕見,許多國中學生在這個階段都面臨類似的拉鋸戰。對孩子而言,會考的意義可能不如即時的娛樂來得具體,而家長的焦慮則來自對未來的長期規劃,兩者的時間視野和優先順序截然不同。</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>AI 告訴我:這種期待與抗拒的衝突,其實反映了親子間的溝通挑戰。家長往往希望孩子能理解會考的重要性,但忽略了孩子可能還在探索自我,尚未完全準備好承擔這樣的壓力。對小幸福來說,手遊和社群媒體不僅是娛樂,也是她與同學互動、建立認同感的方式。完全禁止這些活動可能適得其反,讓她感到被剝奪自由,甚至對學習產生更大的抗拒。</p>
</blockquote>
<p></p>
<p>再加上身處青春期,即使之前在情感銀行裡存了不少存款,要提領出來還是得要小心翼翼。</p>
<p></p>
<p>我認為化解這種衝突的關鍵,在於理解與對話,以及「慢一點」(都焦慮了還要慢一點?)。我開始與小幸福分享我年輕時學習的經驗,以及我認為現在這個時代,學習的方式和工具已經跟過去(我那個年代)不同。我並沒有「現代學習工具」的標準解答,但我試著跟他分享我現在看到的,「供她參考」。</p>
<p></p>
<p>是的,<strong>「供參考」這幾個字很重要</strong>。</p>
<p></p>
<p>就像刷題型一樣,我提供我面對這種題型,以一個四十多歲的大叔…和家長的觀點。但如果她想要用其他的方法解一題,只要解得出來,我還是樂觀其成,頂多再給點意見。</p>
<p>不過就我這一個月來的觀察,與其說是不知道,不如說是「不焦慮」和「不想做」。</p>
<p></p>
<p><strong>不焦慮是相對的,相對於我而言。</strong></p>
<p><strong>不想做這件事,只能靠她自己。</strong></p>
<p></p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>在處於自我認同與獨立性探索的階段,這個年紀的青少年更關注與同儕的社交關係和自我興趣。小幸福對手遊和社群媒體的投入,是她試圖透過這些管道,建立同儕認同和自我價值感。相比之下,會考的壓力對她來說是抽象且遙遠的,這解釋了她為何缺乏我感受到的焦慮。從研究上來看,青少年對即時滿足(instant gratification)的追求往往高於對未來目標的關注,這與大腦前額葉皮質(負責長期規劃和自我控制)的發育尚未成熟有關。(雖然說,成人在現在數位時代的獎勵陷阱中,表現也沒有好到哪兒去)</p>
<p>而,「不想做」,很顯然跟內在動機不足有關。根據自我決定論(Self-Determination Theory, SDT),人類行為的動機來自三種基本心理需求:自主性(autonomy)、勝任/能力感(competence)和歸屬/關係(relatedness)。當孩子感到學習是被迫的,或缺乏自主選擇的空間時,他們的內在動機會下降。</p>
</blockquote>
<p>當家長、學校和補習班幫忙把學習網都架好,良善的動機和想像通常是:「我幫你把工具資源都架好,那麼你學習就輕鬆了。」但學生會這樣想嗎?還是反而會因為這是外部所加,而減緩她內心自我驅動的部份?如果當青少年在學習中缺乏成就感或控制感時,他們會不會更傾向於能快速提供滿足感的活動,如遊戲或社群媒體?</p>
<p>另外,社會比較(social comparison)也可能間接影響小幸福的態度。家長間的討論和對成績的比較,雖然對家長來說是尋求資訊和支持的方式,但可能無意中加劇了孩子的壓力或抗拒。當感受到與他人的比較壓力時,我們可能選擇逃避或轉向其他領域來維護自我價值。所以也有可能,小幸福因為察覺到家長的期待與自身能力之間的差距,而選擇專注於她擅長且能獲得即時回饋的領域,例如遊戲。 (正好也是我相對不擅長的)</p>
<p>最後,數位時代的環境也是一個重要因素。社群媒體和手遊的設計利用了多巴胺驅動的反饋機制(dopamine-driven feedback loops),讓青少年難以抗拒這些即時刺激。大腦對這些高刺激活動的偏好,在注意力有限的情況之下,她就選擇了手機,而不是需要長期投入的學業。</p>
<p></p>
<p>那我該怎麼辦呢?</p>
<p></p>
<h2 class="wp-block-heading">以身作責,最佳化我的注意力</h2>
<p>我是相信身教的。</p>
<p>現在社會中,在餐廳裡,有時看到一家四口坐在一桌,然後各自滑著手機,吃著面前的餐點。這是我儘可能避免的。</p>
<p>為了向小幸福示範注意力和面對長期目標的作法,我開始重新審視資訊接收的習慣。</p>
<p>首先,我開始減少瀏覽社群媒體的時間,尤其是「海巡」式的登入各大社群服務去看今日的熱門話題,以及打開 App 後的無意識滑動。我將主要的資訊來源設定為 RSS 訂閱和電子報,這些管道能提供更精準、結構化的內容,而且來自於我事先選好的範疇。對於 Twitter 和臉書,我刻意保留App 上的廣告。這種使用上的不便和反感,可以降低自己頻繁查看的動力。上週,我還<a href="https://blog.serv.idv.tw/2025/06/techjobntalk-line-comminuty/">退出了幾個 LINE 大群</a>。</p>
<p></p>
<p>再來,我利用自動化工具來簡化例行工作。藉著學習n8n的機會,我打造了一套給我自己用的自動化流程,包括之前提到的 <a href="https://blog.serv.idv.tw/2025/06/n8n-workflow-karakeep/">karakeep 打星分享</a>,以及一些服務的運作狀態通知。理想上,只有在系統出問題時我才會收到通知,其他時候這些檢查都在背景默默運行。這樣的設定讓我能將注意力集中在更有創造性、更重要的任務上。減少定期檢查服務狀態的心思。</p>
<p>製作這些工具的目的,都是要釋放我自己的注意力,專注於重要的事情。</p>
<p></p>
<h2 class="wp-block-heading">以身作責,作長期目標的示範</h2>
<p>這一塊目前我想要用 GTD 和專案管理 的方式來做。之前有稍微跟小幸福聊過 GTD 的方式。對我而言,將這些方法應用於長期目標的管理,我自己已經很純熟,也在職場中跟同事們分享。但如何帶一個青少年實作,而且是在自我內在動機不夠強烈的情況下,我是蠻傷腦筋的。但我還是想要以自己在家裡的作法,讓她看到這種結構化方法的價值。</p>
<p>如果可以,我當然很樂意用這一套方式,為小幸福規劃準備會考的處理方式,例如:</p>
<ul class="wp-block-list">
<li><strong>收集 (Capture)</strong>:讓小幸福把所有要寫的題目、要複習的範圍、要學習的新進度等想法,用筆記本或App記錄下來。</li>
<li><strong>整理 (Clarify)</strong>:將這些想法分類,判斷哪些是立即可做的,哪些需要長期規劃。</li>
<li><strong>組織 (Organize)</strong>:把任務分配到每日或每週計畫中,例如每天花一小時複習數學,每週做一次模擬考。</li>
<li><strong>檢視 (Review)</strong>:每週與她一起回顧進度,看看哪些任務完成了,哪些需要調整。</li>
<li><strong>執行(Do)</strong>:鼓勵她在指定時間 (目前是用番茄鐘方式) 專注完成單一任務,避免分心。</li>
</ul>
<p>但現在這麼做,反而會落入前面提到的陷阱,減損她的自我驅動動機。</p>
<p></p>
<p>所以我目前只有跟他分享 GTD 的架構,與我手上的事情如何用這套方式拆解成 下一步動作 (Next Action, NA),然後實作。我會與她分享我之前如何用 GTD 管理自己的工作,如何分解一個專案並按時完成。我也會和她分享我工作時的筆記和相關工具。主要還是做在我自己身上,示範給她看。</p>
<p>目前我的觀察,GTD 的部份她自己有開始使用小本子作 to list,至少有初步的 Capture 跟 Clarify。但目前還是有點三天打漁兩天曬網。所以… 我只能繼續等待。</p>
<p>而專案管理,那就離他更遠了。</p>
<p>往好處想,即將到來的暑假,延續去年的「<a href="https://blog.serv.idv.tw/2024/07/nomad-wk-11/">貓咪暑假計畫</a>」,今年他們兩位在準備上已經熟門熟路。希望在一起整理下週的任務的同時,可以看得到她的成長。</p>
<p>總之,這一 part 我也是邊做邊看,自己也沒有什麼把握。自己會跟教到別人會,尤其是自己孩子,真是一件不容易的事。</p>
<p></p>
<p></p>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/06/june-playing-with-kids/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/06/880152706046542437.jpeg" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8300</post-id> </item>
<item>
<title>對科技工作講LINE社群的觀察</title>
<link>https://blog.serv.idv.tw/2025/06/techjobntalk-line-comminuty/</link>
<comments>https://blog.serv.idv.tw/2025/06/techjobntalk-line-comminuty/#respond</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Mon, 23 Jun 2025 09:05:00 +0000</pubDate>
<category><![CDATA[社群]]></category>
<category><![CDATA[社會觀察]]></category>
<category><![CDATA[科技工作講]]></category>
<category><![CDATA[管理]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8279</guid>
<description><![CDATA[我應該是在 clubhouse 時期開始收聽科技工作講的,後來隨著這個科技從業者社群的成長和演化,也開始固定聽 ... <a title="對科技工作講LINE社群的觀察" class="read-more" href="https://blog.serv.idv.tw/2025/06/techjobntalk-line-comminuty/" aria-label="Read more about 對科技工作講LINE社群的觀察">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>我應該是在 <a href="https://www.clubhouse.com/">clubhouse</a> 時期開始收聽科技工作講的,後來隨著這個科技從業者社群的成長和演化,<a href="https://linktr.ee/Techjobntalk">也開始固定聽podcast</a>、偶爾聽每週的youtube 直播 (如果題目有興趣,想現場討論的話)。我也追蹤了臉書粉絲頁,閱讀主理人抹布的觀點和言論、加入了LINE 社群、甚至連 telegram 群組都加入過。</p>
<p>這篇文章是針對 科技工作講 LINE 社群的一些觀察和個人觀點。不一定100% 客觀和全面。</p>
<p></p>
<p>簡單說,我要退群了。</p>
<span id="more-8279"></span>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p></p>
<p>我個人當初 加入科技工作講 LINE 社群是期待一個專業建議、每週節目內容討論、以及相互取暖的園地。雖然科技業範圍很大,從半導體設計、製造、到機械、到軟體都有,而角色從RD、PM (這個大宗)、行銷、Q、SCM 都有。真的要找到跟自己100% 背景符合的,要看運氣。但如果放開到「可以互相對話,聽得懂行話」,那還是可以湊齊一隻手 (雖然前前後後,人們來來去去)。</p>
<p>總之,那是一個在工作環境圈子之外,可以分享和傾聽科技業從業生活、科技圈事件和話題、從業者心情分享和抉擇的園地。</p>
<p>至少一開始是如此的。</p>
<p>隨著加入的人數愈來愈多,開始出現小群/專業群,細分成更專業或是特定族群:專業的部份 IC、PM、DA、QA、SWE、AI… 等職業分群出現了;族群部份休閒群、理財群、親子群…也出現了;這還不包括地區性的社群 (如新竹小群) 以及個人私建的群組。這些小群雖然掛著科技工作講的名號,其實是相對獨立的。各個群由熱心的群友創群作為管理者,科技工作講的主理人抹布 moboo 本身不直接管理這些社群,只是在大群裡有記事本和 bot 把有興趣的人導流至這些較小的社群,同時讓這些社群掛上科技工作講的名號。</p>
<p>原始大群在全盛時期接近五千人,因為社群人數的上限還開了二群。現在人數維持四千多人,管理者有5 位,但實際的管板者只有1~2位。科技工作講主理人抹布雖然掛在管理者的名單上,但其實「管板」這件事,主要還是交給熱心的群友。至於抹布參與討論的頻率,就我個人的體感,早期比較多一點,現在已經很少。如果要看抹布說了什麼,或是要和他互動,到科技工作講的粉專還比較有效。其他的管理者有在處理事情的,大概只剩1~2人。4000人的社群,要靠1個管理者管板,就我的觀點和經驗,其實是非常不足的。這個情況下,社群的發展就很吃運氣跟管理者的專業。</p>
<p></p>
<h2 class="wp-block-heading">我觀察到的社群經營問題</h2>
<p>說運氣,是因為有的社群很幸運,會有一些不是管理者的資深群友,主動自發地經營著這個社群的討論和風氣。如果沒那麼幸運的,就反過來變成亂板。亂板的時候,第二道防線就很重要了:管理者的專業。</p>
<p></p>
<h3 class="wp-block-heading">可能的成因之一:不專業的管理者</h3>
<p>說專業,指的不是科技工作專業,而是管理者的公正性和議題經營的方式。4000人的大群,往往會不定期的有爭議性的言論出現。詐騙廣告這種人人喊砍的,不會有什麼問題;但如果是群友的討論,立場不同的時候,管理者往往會劃下一道紅線,讓言論的討論不至於升高到破壞社群風氣的程度。但紅線怎麼劃,管理者如何執行,以及最重要的,管理者如何給人公正客觀,這是管板的專業。</p>
<p>很可惜的,就我的觀察,現在的管理員並沒有做的很好。</p>
<p>舉個群裡最常出現爭議的例子:政治。</p>
<p>在去年年中以後,為了不要讓政治類的話題在大群裡爭吵,設下了一條規定 「政治議題請到政治子群」。</p>
<p>那什麼是「政治議題」呢? 當然是以管理員 Ian 認定。<br>一定開始有人試著踩線,踩邊線,測試那條線的位置。但因為管理者個人的政治觀點和立場,我自己的觀察,這條線是會漂移,會隨著發言的人跟發言的立場而改變的。久而久之,雙方立場的人都觀察到了這個現象,跟管理員同政治立場的人,就快樂地踩一下,踩一下,等到管理員出聲了,再收回,但…不會真的到政治子群去討論。</p>
<p>而對立立場的人,發現檢舉對方談政治議題無效,而自己同樣踩一下踩一下的行為反而為被管理員更嚴格的認定,於是開始主張管理員雙標或是立場不公正。然後…就跟管理員結下樑子了。</p>
<p>至於管理員,當然自己認為自己沒有雙標,自己是中立且客觀,對雙方都一樣的。只是就我的觀察,在爭議性的話題或是政治議題的言論出現後,他有時會先發言講一下觀點、看法、或評論,然後緊接一句:這是政治議題,後續請到政治子群討論。</p>
<p>那這個議題會到政治子群被討論嗎?並沒有。</p>
<p>對立場對立的發言者而言,這個議題在管理者(親某方立場)的發言之後,被沒收(不能再繼續討論)了。</p>
<p></p>
<p>更糟的是,有時(接近政治議題)的討論裡,管理員的發言會讓人分不清楚,他是以管理員的角色在執法/判斷,還是以對立立場在酸/針對/參與。這就像是在路上被警察攔下來,但開口討論的是昨晚你們爭論不休的議題。</p>
<p>你以為他是以個人立場在討論,講著講著他又會以管理者的權限要求討論要平和、有意義、或是不牽涉政治。</p>
<p>你以為他是要出來執法,但他的發言卻又針對某人和過去的恩怨作個人觀點/評判/質疑。</p>
<p></p>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" decoding="async" width="1162.5" height="655" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/%E6%B2%92%E6%94%B6%E5%8A%9F%E5%B0%B1%E7%BD%B5%E9%AB%92%E8%A9%B1_L.jpg?resize=1162.5%2C655&ssl=1" alt="" class="wp-image-8284" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/%E6%B2%92%E6%94%B6%E5%8A%9F%E5%B0%B1%E7%BD%B5%E9%AB%92%E8%A9%B1_L.jpg?w=2400&ssl=1 2400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/%E6%B2%92%E6%94%B6%E5%8A%9F%E5%B0%B1%E7%BD%B5%E9%AB%92%E8%A9%B1_L.jpg?resize=400%2C225&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/%E6%B2%92%E6%94%B6%E5%8A%9F%E5%B0%B1%E7%BD%B5%E9%AB%92%E8%A9%B1_L.jpg?resize=1024%2C577&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/%E6%B2%92%E6%94%B6%E5%8A%9F%E5%B0%B1%E7%BD%B5%E9%AB%92%E8%A9%B1_L.jpg?resize=1536%2C865&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/%E6%B2%92%E6%94%B6%E5%8A%9F%E5%B0%B1%E7%BD%B5%E9%AB%92%E8%A9%B1_L.jpg?resize=2048%2C1154&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/%E6%B2%92%E6%94%B6%E5%8A%9F%E5%B0%B1%E7%BD%B5%E9%AB%92%E8%A9%B1_L.jpg?w=2325&ssl=1 2325w" sizes="(max-width: 1162px) 100vw, 1162px" /></figure></div>
<p>這些其實一直都是社群/管板者要面對的問題。也因為如此,早期 (從 BBS 年代) 針對這種角色上可能的混淆,就已經有一些應對的措施和規範。包括獨立的管理者帳號,或是將執法和個人的發言作清楚的區分等等。</p>
<p>在職場上來說也是,為什麼你的好同事升官換了個位子,就不跟你一起在座位嘲笑公司政策和那些過去你們一起看不慣的同事了? </p>
<p>「換個位子,換個腦袋」沒錯。但這句話,除了字面上的貶意之外,還有更深的用意在裡頭。</p>
<p></p>
<p>簡單來說,當你擁有權力的同時,也要特別小心維持管理者/執法者的公正性,以及公私的分明。</p>
<p>瓜田李下,古早的成語就是人性的寫照。</p>
<p>在現實生活中,警察的制服、公家機關謝絕選舉造熱或政治活動,都是同樣的出發點。</p>
<p></p>
<p>以我的觀點而言,現在管板者的個人好惡、立場、以及一些發言,已經影響到這個社群的討論健康和多元。</p>
<p>我觀察到一些具有貢獻的群友離開,不是因為對「科技工作講」這個主題不感興趣,而是因為跟其他群友的衝突 — 包括管板的 Ian。</p>
<p>這是一個有趣的現象:好的社群管理者應該保障討論的多元和有機,而不是「順耳」的言論。</p>
<p>當我看到管理者在和志同道合者嘻嘻哈哈的同時,一邊批評著對立觀點的人,他頭像旁的那顆閃亮小皇冠 (具有管理者權力的標示) 真的讓我搖頭。</p>
<p></p>
<p>接下來被影響到的,就是風氣經營了。</p>
<p></p>
<h3 class="wp-block-heading">可能的成因之二:科技工作講主理人的態度</h3>
<p>4000人的 LINE 社群,遇到爭議時,沒有一個相對公正的管理;而創群/科技工作講的主理人,抹布,他對於這個社群的想法又是如何呢?</p>
<p>這個社群雖然掛上科技工作講的名號,但在 2024年5月的深夜,主理人抹布有說過因為當時太忙,他其實是放掉這個社群的。</p>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="1024" height="766" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/IMG_8033.jpg?resize=1024%2C766&ssl=1" alt="去年 moboo 的發言" class="wp-image-8281" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/IMG_8033.jpg?resize=1024%2C766&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/IMG_8033.jpg?resize=400%2C299&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/IMG_8033.jpg?w=1179&ssl=1 1179w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>對,因為沒有時間經營<br>去年接主管之後晚上還要跟美國溝通,所以改成開社團讓大家留言,去年時間還比較多,<br>今年因為合併的新的團隊,現在我還要跟軟體溝通,必須要放掉這邊</p>
</blockquote>
<p></p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>我感謝你提出想法,其實你知道我是看很長線的人,這某種程度是我選擇先不處理Line群的原因,因為他處理不完。我應該專心在把粉絲團弄大,把Podcast維持下去,就可以弄到更多資源,像是很多科技公司一直想贊助我們下半年活動。我目標是看長看遠</p>
<p>我理解到大家對於一些人發言感到很不滿,我也跟蘇桑當面說過請他克制一點。之間也跟幾個管理員討論過。其實他們都有在管理。目前是沒有比較強硬把一些人踢出去。這個尺度很難拿捏</p>
<p>我目前是沒有要求要怎麼做<br>是最近tag我的人變多,我才來,但是前幾天讀這群都當<img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f602.png" alt="😂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
</blockquote>
<p>而一年過去了,抹布依舊在臉書粉絲團活躍著,每天都會看到他的文章或發言;但在LINE 社群裡的參與討論或回應,已經是快兩個月前的事情了。因此「放掉LINE 社群」這件事,應該還是跟去年年中時的想法/作法一致。</p>
<p>也因為這樣,雖然是「科技工作講」 LINE 社群,但跟抹布並沒有太大的關係。過去在直播的同時,LINE 這邊還會有同步的討論,但已經很久沒看到了。這裡的風氣經營、議題設定和發展,已經偏離當初一開始我加入的狀態了。</p>
<p></p>
<h2 class="wp-block-heading">那現在討論的風氣如何?</h2>
<p>就我的體感,現在大群變成另一種…轉貼然後進行時事和國際情勢評論的地方,而且聊著聊著就會往政治那條線靠近,然後管理員再出來沒收討論… XD</p>
<p></p>
<p>為了確認我的體感是否正確,請 ChatGPT 統計了過去14天的話題中,前幾大分類:</p>
<ul class="wp-block-list">
<li>職涯發展與工作選擇:這是開群以來的熱門話題,約占兩成。</li>
<li>社會現象和政治/政策評論:也占兩成左右</li>
<li>產業與經濟現象:這不限於科技業,話題約 15%</li>
<li>AI 和科技話題:大概 7%</li>
</ul>
<p></p>
<p>若以發言者的次數來看,其中 80% 的發言集中在5個人身上。以一個4200人的社群而言… 有點怪異。</p>
<p>如果要細緻或深入一些的討論,還是可以往小群跑。但這很吃小群管理者的風格。而且發言集中在少數幾個人的狀況,在小群其實更加嚴重。每個小群的素質也有顯著的差異。有的已經變成幾個老群友的聊天群了。</p>
<p></p>
<p></p>
<h2 class="wp-block-heading">那未來呢</h2>
<p>因為這個社群已經4000人,以目前科技業的熱門程度而言,這個群短時間並不會消滅。</p>
<p>一個社群即使已經可以自己維持生命,要保持健康,我的想法是要討論內容要有機(organic)和保持多樣性(diversity)。</p>
<p></p>
<p>以我目前觀察到的「活躍用戶」占的比例,以社群現任管理者面對爭議處理的方式,以抹布對這個LINE社群的態度 (放掉對這個社群的認真經營),不要說是人數不會成長,這個群的「健康」已經下降 (腐敗) 到我也變成吃瓜壁上觀的一員了。</p>
<p></p>
<p>即使我跟其他4000人一樣,多數時間只是潛水的大眾;即使這個群偶爾會有具意義和價值的討論,讓我願意貢獻我的兩分錢;即使我使用這個社群的方式是看一下最近討論的話題,並沒有爬過每一則討論;寫文現況的我,仍然覺得這樣子做,浪費了我稀缺的注意力。</p>
<p></p>
<p>因此,我覺得跟這個群分手的時間到了。</p>
<p>podcast 我還是會繼續聽,抹布在粉專的話題也會常常被臉書推到我面前 (但點不點進去,取決於我),部份小群還是會待著,留意一下對科技話題的看法。</p>
<p>至於已經變質的大群或是太水的小群…就先道再見了。</p>
<p></p>
<p></p>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/06/techjobntalk-line-comminuty/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/06/banner-社群經營.jpeg" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8279</post-id> </item>
<item>
<title>Colab + WhisperX 將音檔轉成逐字稿 (20250619版)</title>
<link>https://blog.serv.idv.tw/2025/06/colab-whisperx-transcript-revised-50619/</link>
<comments>https://blog.serv.idv.tw/2025/06/colab-whisperx-transcript-revised-50619/#comments</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Thu, 19 Jun 2025 03:01:26 +0000</pubDate>
<category><![CDATA[電腦.網路]]></category>
<category><![CDATA[AI]]></category>
<category><![CDATA[podcast]]></category>
<category><![CDATA[Python]]></category>
<category><![CDATA[vibe coding]]></category>
<category><![CDATA[逐字稿]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8274</guid>
<description><![CDATA[這兩天有需求,再上 colab 用 WhisperX 把音檔轉成逐字稿。發現之前寫好的code 又跑出錯誤訊息 ... <a title="Colab + WhisperX 將音檔轉成逐字稿 (20250619版)" class="read-more" href="https://blog.serv.idv.tw/2025/06/colab-whisperx-transcript-revised-50619/" aria-label="Read more about Colab + WhisperX 將音檔轉成逐字稿 (20250619版)">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>這兩天有需求,再上 colab 用 WhisperX 把音檔轉成逐字稿。發現之前<a href="https://blog.serv.idv.tw/2025/04/colab-whisperx-transcript/">寫好的code </a>又跑出錯誤訊息了。</p>
<p>解決問題的路上,發現新版已經跑得起來,不用像之前還要移除 Pytorch、安裝特定相容的版本。但版本依賴的狀況還是蠻混亂的。</p>
<p>新的版本還是帶來新的問題:</p>
<ul class="wp-block-list">
<li>whisperx.DiarizationPipeline 指令改成 whisperx.diarize.DiarizationPipeline</li>
<li>alignment.py 裡有個Index Error,<a href="https://github.com/m-bain/whisperX/issues/1048">Issue 裡也有人回報</a>,但還在等修復,目前需要自己進去改code。</li>
</ul>
<p>我整理了一下,再把新的code 放上來,但使用上要小心。再幾個版本後可能又不能用了。</p>
<p></p>
<p>目前日期 (2025/6/19)所安裝的版本為:</p>
<pre class="wp-block-preformatted">whisperx 3.3.4<br>ctranslate2 4.4.0<br>pyannote-audio 3.3.2<br>torch 2.6.0+cu124<br>torchaudio 2.6.0+cu124<br>libcudnn8 8.9.7.29-1+cuda12.2<br>libcudnn8-dev 8.9.7.29-1+cuda12.2</pre>
<p></p>
<p>以下是目前的code 跟修改的地方。如果要知道更多 code 的作用,可以回去參考<a href="https://blog.serv.idv.tw/2025/04/colab-whisperx-transcript/">一開始發佈的版本</a>,裡頭有說明。</p>
<span id="more-8274"></span>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">Part 1. 掛載 Google Drive</h2>
<p>這部份沒有什麼改變。</p>
<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="複製" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>from google.colab import drive
drive.mount(‘/content/drive’)</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> google</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">colab </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> drive</span></span>
<span class="line"><span style="color: #D8DEE9FF">drive</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">mount</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">/content/drive</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">)</span></span></code></pre></div>
<p></p>
<p></p>
<h2 class="wp-block-heading">Part 2. 安裝與環境配置</h2>
<p>新版變得簡潔許多了。只要記得安裝 libduenn8、whisperx、跟pyannote.audio. </p>
<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="複製" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>!apt-get update
!apt install libcudnn8 libcudnn8-dev -y
!pip install whisperx
!pip install pyannote.audio
!python -c “import torch; torch.backends.cuda.matmul.allow_tf32 = True; torch.backends.cudnn.allow_tf32 = True”</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">apt</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">get update</span></span>
<span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">apt install libcudnn8 libcudnn8</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">dev </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">y</span></span>
<span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">pip install whisperx</span></span>
<span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">pip install pyannote</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">audio</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">python </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">c </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">import torch; torch.backends.cuda.matmul.allow_tf32 = True; torch.backends.cudnn.allow_tf32 = True</span><span style="color: #ECEFF4">"</span></span></code></pre></div>
<p></p>
<h2 class="wp-block-heading">Part 3. 修改alignment.py 原始碼</h2>
<p>這裡因為要修改 alignment.py 裡的一行原始碼,解決出現 <code>IndexError: tensors used as indices must be long, int, byte or bool tensors </code>的問題,所以需要寫個腳本處理。如果未來 whisperx 新版解決掉這個bug,這一段可能就不需要了。</p>
<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="複製" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly># 重置環境(可選)
#%reset -f
# 安裝或更新 whisperx(如果需要)
#!pip install –upgrade whisperx
# 複製 whisperx 到 /content
!cp -r /usr/local/lib/python3.11/dist-packages/whisperx /content/whisperx
# 修改 alignment.py
import os
file_path = ‘/content/whisperx/alignment.py’
with open(file_path, ‘r’) as file:
lines = file.readlines()
target_line = ‘regular_scores = frame_emission[tokens.clamp(min=0)]’
new_line = ‘regular_scores = frame_emission[tokens.clamp(min=0).long()] # 轉換為 torch.long\n’
for i, line in enumerate(lines):
if target_line in line:
lines[i] = new_line
break
with open(file_path, ‘w’) as file:
file.writelines(lines)
print(“已修改 alignment.py”)
# 驗證修改
!grep “regular_scores = frame_emission” /content/whisperx/alignment.py
# 清除模組快取
import sys
for module in list(sys.modules.keys()):
if module.startswith(‘whisperx’):
del sys.modules[module]
print(“已清除 whisperx 相關模組”)
# 設置 sys.path
sys.path.insert(0, ‘/content’)
print(“sys.path:”, sys.path)
# 載入並驗證 whisperx
import whisperx
print(“whisperx 路徑:”, whisperx.__file__) # 應為 /content/whisperx/__init__.py
import os
file_path = ‘/content/whisperx/alignment.py’
with open(file_path, ‘r’) as file:
lines = file.readlines()
# 修復 regular_scores 的縮進
for i, line in enumerate(lines):
if ‘regular_scores = frame_emission[tokens.clamp(min=0).long()]’ in line:
lines[i] = ‘ ‘ + line.lstrip() # 設置為 4 個空格
break
# 寫回文件
with open(file_path, ‘w’) as file:
file.writelines(lines)
print(“已修復 regular_scores 的縮進”)
# 驗證修改
!sed -n ‘420,435p’ /content/whisperx/alignment.py
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88"># 重置環境(可選)</span></span>
<span class="line"><span style="color: #616E88">#%reset -f</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 安裝或更新 whisperx(如果需要)</span></span>
<span class="line"><span style="color: #616E88">#!pip install --upgrade whisperx</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 複製 whisperx 到 /content</span></span>
<span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">cp </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">r </span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">usr</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">local</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">lib</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">python3</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">11</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">dist</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">packages</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">whisperx </span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">content</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">whisperx</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 修改 alignment.py</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> os</span></span>
<span class="line"><span style="color: #D8DEE9FF">file_path </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">/content/whisperx/alignment.py</span><span style="color: #ECEFF4">'</span></span>
<span class="line"><span style="color: #81A1C1">with</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">open</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">file_path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">r</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> file</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> lines </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">readlines</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">target_line </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">regular_scores = frame_emission[tokens.clamp(min=0)]</span><span style="color: #ECEFF4">'</span></span>
<span class="line"><span style="color: #D8DEE9FF">new_line </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">regular_scores = frame_emission[tokens.clamp(min=0).long()] # 轉換為 torch.long</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">'</span></span>
<span class="line"><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> i</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> line </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">enumerate</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">lines</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> target_line </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> line</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> lines</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">i</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new_line</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">break</span></span>
<span class="line"><span style="color: #81A1C1">with</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">open</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">file_path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">w</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> file</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">writelines</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">lines</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">已修改 alignment.py</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 驗證修改</span></span>
<span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">grep </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">regular_scores = frame_emission</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">content</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">whisperx</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">alignment</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">py</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 清除模組快取</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> sys</span></span>
<span class="line"><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> module </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">list</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">sys</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">modules</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">keys</span><span style="color: #ECEFF4">()):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> module</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">startswith</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">whisperx</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">del</span><span style="color: #D8DEE9FF"> sys</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">modules</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">module</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">已清除 whisperx 相關模組</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 設置 sys.path</span></span>
<span class="line"><span style="color: #D8DEE9FF">sys</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">path</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">insert</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">/content</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">sys.path:</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> sys</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">path</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 載入並驗證 whisperx</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> whisperx</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">whisperx 路徑:</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> whisperx</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">__file__</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 應為 /content/whisperx/__init__.py</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> os</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">file_path </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">/content/whisperx/alignment.py</span><span style="color: #ECEFF4">'</span></span>
<span class="line"><span style="color: #81A1C1">with</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">open</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">file_path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">r</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> file</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> lines </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">readlines</span><span style="color: #ECEFF4">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 修復 regular_scores 的縮進</span></span>
<span class="line"><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> i</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> line </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">enumerate</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">lines</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">regular_scores = frame_emission[tokens.clamp(min=0).long()]</span><span style="color: #ECEFF4">'</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> line</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> lines</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">i</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">'</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> line</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">lstrip</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 設置為 4 個空格</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">break</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 寫回文件</span></span>
<span class="line"><span style="color: #81A1C1">with</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">open</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">file_path</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">w</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> file</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">writelines</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">lines</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">已修復 regular_scores 的縮進</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 驗證修改</span></span>
<span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">sed </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF">n </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">420,435p</span><span style="color: #ECEFF4">'</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">content</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">whisperx</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">alignment</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">py</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>
<p></p>
<h2 class="wp-block-heading">Part 4: 載入函式庫與參數設定</h2>
<p>這裡沒什麼變,主要改的是 whisperx.DiarizationPipeline 要改成whisperx.diarize.DiarizationPipeline。</p>
<p>其他維持現狀:</p>
<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="複製" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import whisperx
import torch
import gc
import os
# from faster_whisper import WhisperModel
from tqdm import tqdm
import os
from google.colab import files
from google.colab import userdata
model_size = “large-v2″ # tiny, base, small, medium, large, large-v2, large-v3
batch_size = 16 # reduce if low on GPU mem
device=”cuda”
# 設定檔案路徑
audio_path = “/content/drive/MyDrive/Colab Notebooks/.mp3” # 替換成你的檔案名稱
HF_TOKEN = userdata.get(‘HF_TOKEN’)
os.environ[‘HUGGING_FACE_HUB_TOKEN’] = HF_TOKEN
# 1. Transcribe with original whisper (batched)
print(“正在載入 Whisper 模型…”)
model = whisperx.load_model(model_size, device, compute_type=”float16″)
print(f”正在載入音訊檔案: {audio_path}”)
audio = whisperx.load_audio(audio_path)
print(“正在進行轉錄…”)
result = model.transcribe(audio, batch_size=batch_size, chunk_size=6)
# print( result[“segments”]) # before alignment
# 2. Align whisper output
print(“正在載入對齊模型…”)
model_a, metadata = whisperx.load_align_model(language_code=result[“language”], device=device)
aligned_result = whisperx.align(result[“segments”], model_a, metadata, audio, device, return_char_alignments=True, interpolate_method=”linear”)
# 3. Assign speaker labels
print(“正在載入說話人分割模型…”)
diarize_model = whisperx.diarize.DiarizationPipeline(device=device)
diarize_segments = diarize_model(audio)
# add min/max number of speakers if known
# diarize_segments = diarize_model(audio, min_speakers=1, max_speakers=2)
# print(diarize_segments)
print(“正在將說話人標籤分配給詞語…”)
final_result = whisperx.assign_word_speakers(diarize_segments, aligned_result)
# print(final_result[“segments”]) # segments are now assigned speaker IDs
print(“\n— 最終結果 (片段與說話人) —“)
# for segment in final_result[“segments”]:
# speaker = segment.get(‘speaker’, ‘未知說話人’)
# start_time = segment[‘start’]
# end_time = segment[‘end’]
# text = segment[‘text’]
# print(f”[{start_time:.2f}s – {end_time:.2f}s] {speaker}: {text}”)
# — 清理記憶體 —
#del model, model_a, diarize_model, result, aligned_result, final_result, audio
gc.collect()
torch.cuda.empty_cache()
print(“\n已清理所有模型和變數的記憶體.”)
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> whisperx</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> torch</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> gc</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> os</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># from faster_whisper import WhisperModel</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> tqdm </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> tqdm</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> os</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> google</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">colab </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> files</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> google</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">colab </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> userdata</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">model_size </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">large-v2</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># tiny, base, small, medium, large, large-v2, large-v3</span></span>
<span class="line"><span style="color: #D8DEE9FF">batch_size </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">16</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># reduce if low on GPU mem</span></span>
<span class="line"><span style="color: #D8DEE9FF">device</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">cuda</span><span style="color: #ECEFF4">"</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 設定檔案路徑</span></span>
<span class="line"><span style="color: #D8DEE9FF">audio_path </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">/content/drive/MyDrive/Colab Notebooks/.mp3</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 替換成你的檔案名稱</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">HF_TOKEN </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> userdata</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">HF_TOKEN</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">environ</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">HUGGING_FACE_HUB_TOKEN</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> HF_TOKEN</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 1. Transcribe with original whisper (batched)</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">正在載入 Whisper 模型...</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">model </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> whisperx</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">load_model</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">model_size</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> device</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">compute_type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">float16</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"正在載入音訊檔案: </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">audio_path</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">audio </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> whisperx</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">load_audio</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">audio_path</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">正在進行轉錄...</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">result </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> model</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">transcribe</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">audio</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">batch_size</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">batch_size</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">chunk_size</span><span style="color: #81A1C1">=</span><span style="color: #B48EAD">6</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #616E88"># print( result["segments"]) # before alignment</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 2. Align whisper output</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">正在載入對齊模型...</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">model_a</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> metadata </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> whisperx</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">load_align_model</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">language_code</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">result</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">language</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">],</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">device</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">device</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">aligned_result </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> whisperx</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">align</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">result</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">segments</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">],</span><span style="color: #D8DEE9FF"> model_a</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> metadata</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> audio</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> device</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">return_char_alignments</span><span style="color: #81A1C1">=True</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">interpolate_method</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">linear</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 3. Assign speaker labels</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">正在載入說話人分割模型...</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">diarize_model </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> whisperx</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">diarize</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">DiarizationPipeline</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">device</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">device</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">diarize_segments </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">diarize_model</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">audio</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #616E88"># add min/max number of speakers if known</span></span>
<span class="line"><span style="color: #616E88"># diarize_segments = diarize_model(audio, min_speakers=1, max_speakers=2)</span></span>
<span class="line"><span style="color: #616E88"># print(diarize_segments)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">正在將說話人標籤分配給詞語...</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">final_result </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> whisperx</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">assign_word_speakers</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">diarize_segments</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> aligned_result</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #616E88"># print(final_result["segments"]) # segments are now assigned speaker IDs</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #EBCB8B">\n</span><span style="color: #A3BE8C">--- 最終結果 (片段與說話人) ---</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># for segment in final_result["segments"]:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># speaker = segment.get('speaker', '未知說話人')</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># start_time = segment['start']</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># end_time = segment['end']</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># text = segment['text']</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># print(f"[{start_time:.2f}s - {end_time:.2f}s] {speaker}: {text}")</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># --- 清理記憶體 ---</span></span>
<span class="line"><span style="color: #616E88">#del model, model_a, diarize_model, result, aligned_result, final_result, audio</span></span>
<span class="line"><span style="color: #D8DEE9FF">gc</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">collect</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">torch</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">cuda</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">empty_cache</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #EBCB8B">\n</span><span style="color: #A3BE8C">已清理所有模型和變數的記憶體.</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span></code></pre></div>
<p></p>
<h2 class="wp-block-heading">Part 4: 合併同講者詞與 GPT-4 斷句</h2>
<p>這裡也沒什麼改變,只是把 Chat GPT model 改成新的/較便宜的 gpt-4.1-nano 而已。</p>
<p>但其實切字部份code還有改善的空間。</p>
<p></p>
<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(3 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="複製" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>!pip install openai
print(“\n————— 中文merge 與斷句處理 ———\n”)
import openai
import time
import os
HF_TOKEN = userdata.get(‘OPENAI_API’)
os.environ[‘OPENAI_API_KEY’] = HF_TOKEN
##########################################
# 先把同一個發言者的字合併
def merge_words_by_speaker(segments):
merged = []
current = {
“speaker”: None,
“text”: “”,
“start”: None,
“end”: None
}
pending_unknown = None # 用來暫存沒有時間的 UNKNOWN 詞
for segment in segments:
for word in segment.get(“words”, []):
speaker = word.get(“speaker”, “UNKNOWN”)
word_text = word[“word”]
# 處理 UNKNOWN 且沒時間標記的詞
if speaker == “UNKNOWN” and “start” not in word and “end” not in word:
# 當作當前 speaker 的延伸,先暫存
current[“text”] += word_text
pending_unknown = word_text
continue
# speaker 改變時,儲存上一段
if current[“speaker”] != speaker:
if current[“text”]:
merged.append(current)
# 如果有 pending_unknown,要接到下一個 speaker 的開頭
if pending_unknown:
word_text = pending_unknown + word_text
pending_unknown = None
current = {
“speaker”: speaker,
“text”: word_text,
“start”: word.get(“start”),
“end”: word.get(“end”)
}
else:
# 同一位 speaker 的詞
pending_unknown = None
current[“text”] += word_text
if “end” in word:
current[“end”] = word[“end”]
# 加入最後一段
if current[“text”]:
merged.append(current)
return merged
# 用 GPT-4 做斷句處理
client = openai.OpenAI() # 用環境變數設定 OPENAI_API_KEY
def punctuate_with_gpt(text, model=”gpt-4.1-nano”, max_retry=3):
prompt = f”””請幫我將以下沒有標點的中文話語補上合適的標點(例如句號、逗號、問號等),並分成自然語言語句:
{text}
輸出時只需要修正後的文本,不需要其他解釋。”””
for _ in range(max_retry):
try:
response = client.chat.completions.create(
model=model,
messages=[{“role”: “user”, “content”: prompt}],
temperature=0.3
)
return response.choices[0].message.content.strip()
except Exception as e:
print(f”API error: {e}”)
time.sleep(2)
return text
# 對合併後的結果做 GPT 斷句處理
def process_segments_with_gpt(merged_results, length_threshold=10):
processed = []
for segment in merged_results:
raw_text = segment[‘text’]
if len(raw_text) >= length_threshold:
processed_text = punctuate_with_gpt(raw_text)
else:
processed_text = raw_text
processed.append({
“speaker”: segment.get(“speaker”, “未知說話人”),
“start”: segment.get(“start”),
“end”: segment.get(“end”),
“text”: processed_text
})
return processed
# Step 4: 輸出結果
def print_segments(segments):
for seg in segments:
start = seg[“start”]
end = seg[“end”]
speaker = seg[“speaker”]
text = seg[“text”]
print(f”[{start:.2f}s – {end:.2f}s] {speaker}: {text}”)
# 使用方式
print(“正在合併同一個發言者的發言…\n”)
merged_results = merge_words_by_speaker(final_result[“segments”])
print(“正在使用 GPT 作斷句和標點處理……\n”)
final_sentences = process_segments_with_gpt(merged_results)
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">!</span><span style="color: #D8DEE9FF">pip install openai</span></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #EBCB8B">\n</span><span style="color: #A3BE8C">--------------- 中文merge 與斷句處理 ---------</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> openai</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> time</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> os</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">HF_TOKEN </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> userdata</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">OPENAI_API</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">environ</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">OPENAI_API_KEY</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> HF_TOKEN</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">##########################################</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 先把同一個發言者的字合併</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">merge_words_by_speaker</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">segments</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> merged </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> current </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">None</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">""</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">None</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">None</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> pending_unknown </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">None</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 用來暫存沒有時間的 UNKNOWN 詞</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> segment </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> segments</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> word </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> segment</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">words</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[]):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> speaker </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> word</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">UNKNOWN</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> word_text </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> word</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">word</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 處理 UNKNOWN 且沒時間標記的詞</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> speaker </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">UNKNOWN</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">and</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">not</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> word </span><span style="color: #81A1C1">and</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">not</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> word</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 當作當前 speaker 的延伸,先暫存</span></span>
<span class="line"><span style="color: #D8DEE9FF"> current</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> word_text</span></span>
<span class="line"><span style="color: #D8DEE9FF"> pending_unknown </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> word_text</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">continue</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># speaker 改變時,儲存上一段</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> current</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> speaker</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> current</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> merged</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">append</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">current</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 如果有 pending_unknown,要接到下一個 speaker 的開頭</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> pending_unknown</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> word_text </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> pending_unknown </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> word_text</span></span>
<span class="line"><span style="color: #D8DEE9FF"> pending_unknown </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">None</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> current </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> speaker</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> word_text</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> word</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">),</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> word</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 同一位 speaker 的詞</span></span>
<span class="line"><span style="color: #D8DEE9FF"> pending_unknown </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">None</span></span>
<span class="line"><span style="color: #D8DEE9FF"> current</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> word_text</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> word</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> current</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> word</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 加入最後一段</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> current</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> merged</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">append</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">current</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> merged</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 用 GPT-4 做斷句處理</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">client </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> openai</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">OpenAI</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 用環境變數設定 OPENAI_API_KEY</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">punctuate_with_gpt</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">text</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">model</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">gpt-4.1-nano</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">max_retry</span><span style="color: #81A1C1">=</span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> prompt </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"""請幫我將以下沒有標點的中文話語補上合適的標點(例如句號、逗號、問號等),並分成自然語言語句:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">text</span><span style="color: #EBCB8B">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #A3BE8C">輸出時只需要修正後的文本,不需要其他解釋。"""</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> _ </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">range</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">max_retry</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> response </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> client</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">chat</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">completions</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">create</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">model</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">model</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">messages</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">[{</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">role</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">user</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">content</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> prompt</span><span style="color: #ECEFF4">}],</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">temperature</span><span style="color: #81A1C1">=</span><span style="color: #B48EAD">0.3</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> response</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">choices</span><span style="color: #ECEFF4">[</span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">].</span><span style="color: #D8DEE9FF">message</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">content</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">strip</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">except</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Exception</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> e</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"API error: </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">e</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> time</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">sleep</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> text</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 對合併後的結果做 GPT 斷句處理</span></span>
<span class="line"><span style="color: #81A1C1">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">process_segments_with_gpt</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">merged_results</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length_threshold</span><span style="color: #81A1C1">=</span><span style="color: #B48EAD">10</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> processed </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> segment </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> merged_results</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> raw_text </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> segment</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">'</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">len</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">raw_text</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">>=</span><span style="color: #D8DEE9FF"> length_threshold</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> processed_text </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">punctuate_with_gpt</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">raw_text</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> processed_text </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> raw_text</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> processed</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">append</span><span style="color: #ECEFF4">({</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> segment</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">未知說話人</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">),</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> segment</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">),</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> segment</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">get</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">),</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> processed_text</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> processed</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># Step 4: 輸出結果</span></span>
<span class="line"><span style="color: #81A1C1">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">print_segments</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">segments</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> seg </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> segments</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> start </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> end </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> speaker </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> text </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"[</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">start</span><span style="color: #81A1C1">:.2f</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">s - </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">end</span><span style="color: #81A1C1">:.2f</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">s] </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">speaker</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">: </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">text</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 使用方式</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">正在合併同一個發言者的發言...</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">merged_results </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">merge_words_by_speaker</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">final_result</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">segments</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">])</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">正在使用 GPT 作斷句和標點處理…...</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">final_sentences </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">process_segments_with_gpt</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">merged_results</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>
<p></p>
<h2 class="wp-block-heading">Part 6. 輸出與儲存結果</h2>
<p>這部份也沒什麼改變。</p>
<p>多產生一個沒有時間碼的版本而已。</p>
<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="複製" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>print(“正在輸出結果…\n”)
# print_segments(final_sentences) # 顯示結果
# save_segments_to_txt(final_sentences, filename=”transcription_result.txt”) # 儲存結果
# Step 5: 儲存結果到文字檔
def format_time(seconds):
if seconds is None:
return “??:??:??”
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
seconds = int(seconds % 60)
return f”{hours:02}:{minutes:02}:{seconds:02}”
# 獲取不帶副檔名的檔案名稱
filename_orig = os.path.splitext(os.path.basename(audio_path))[0]
filename_orig = filename_orig + “.txt”
filename2_orig = filename_orig + “-notimecode.txt”
def save_segments_to_txt(segments, filename=filename_orig):
with open(filename, “w”, encoding=”utf-8″) as f:
for seg in segments:
start = format_time(seg[“start”])
end = format_time(seg[“end”])
speaker = seg[“speaker”]
text = seg[“text”]
f.write(f”[{start} – {end}] {speaker}: {text}\n”)
files.download(f”{filename}”)
print(f”儲存成功:{filename}”)
def save_segments_to_txt2(segments, filename=filename2_orig):
with open(filename, “w”, encoding=”utf-8″) as f:
for seg in segments:
start = format_time(seg[“start”])
end = format_time(seg[“end”])
speaker = seg[“speaker”]
text = seg[“text”]
f.write(f” {text}”)
files.download(f”{filename}”)
print(f”儲存成功:{filename}”)
# save_segments_to_txt(final_sentences, filename=”transcription_result.txt”) # 儲存結果
save_segments_to_txt(final_sentences) # 儲存結果
save_segments_to_txt2(final_sentences) # 儲存結果
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">正在輸出結果...</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #616E88"># print_segments(final_sentences) # 顯示結果</span></span>
<span class="line"><span style="color: #616E88"># save_segments_to_txt(final_sentences, filename="transcription_result.txt") # 儲存結果</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># Step 5: 儲存結果到文字檔</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">format_time</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">seconds</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> seconds </span><span style="color: #81A1C1">is</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">None</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">??:??:??</span><span style="color: #ECEFF4">"</span></span>
<span class="line"><span style="color: #D8DEE9FF"> hours </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">int</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">seconds </span><span style="color: #81A1C1">//</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3600</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> minutes </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">int</span><span style="color: #ECEFF4">((</span><span style="color: #D8DEE9FF">seconds </span><span style="color: #81A1C1">%</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3600</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">//</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> seconds </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">int</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">seconds </span><span style="color: #81A1C1">%</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">hours</span><span style="color: #81A1C1">:02</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">:</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">minutes</span><span style="color: #81A1C1">:02</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">:</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">seconds</span><span style="color: #81A1C1">:02</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># 獲取不帶副檔名的檔案名稱</span></span>
<span class="line"><span style="color: #D8DEE9FF">filename_orig </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">path</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">splitext</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">os</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">path</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">basename</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">audio_path</span><span style="color: #ECEFF4">))[</span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF">filename_orig </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> filename_orig </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">.txt</span><span style="color: #ECEFF4">"</span></span>
<span class="line"><span style="color: #D8DEE9FF">filename2_orig </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> filename_orig </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">-notimecode.txt</span><span style="color: #ECEFF4">"</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">save_segments_to_txt</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">segments</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">filename</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">filename_orig</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">with</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">open</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">filename</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">w</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">encoding</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">utf-8</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> f</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> seg </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> segments</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> start </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">format_time</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF"> end </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">format_time</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF"> speaker </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> text </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> f</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">write</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"[</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">start</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C"> - </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">end</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">] </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">speaker</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">: </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">text</span><span style="color: #EBCB8B">}\n</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> files</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">download</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">filename</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"儲存成功:</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">filename</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">save_segments_to_txt2</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">segments</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">filename</span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF">filename2_orig</span><span style="color: #ECEFF4">):</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">with</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">open</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">filename</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">w</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">encoding</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">utf-8</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> f</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> seg </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> segments</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF"> start </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">format_time</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">start</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF"> end </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">format_time</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">end</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">])</span></span>
<span class="line"><span style="color: #D8DEE9FF"> speaker </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">speaker</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> text </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> seg</span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">text</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF"> f</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">write</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">" </span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">text</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> files</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">download</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">filename</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">f</span><span style="color: #A3BE8C">"儲存成功:</span><span style="color: #EBCB8B">{</span><span style="color: #D8DEE9FF">filename</span><span style="color: #EBCB8B">}</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># save_segments_to_txt(final_sentences, filename="transcription_result.txt") # 儲存結果</span></span>
<span class="line"><span style="color: #88C0D0">save_segments_to_txt</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">final_sentences</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 儲存結果</span></span>
<span class="line"><span style="color: #88C0D0">save_segments_to_txt2</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">final_sentences</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># 儲存結果</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>
<p></p>
<p>WhisperX 是一個還在發展進化的專案,不過我現在碰到的問題主要是切字的效果。</p>
<p>另外判斷speaker 的部份,中文還是會受到音質的影響,體感覺得成功率大概只有8~9成。這部份因為使用的頻率不夠高,後面需要再手動處理過。真的還不到無腦輸出的地步。</p>
<p></p>
<p>就將就著用吧!</p>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/06/colab-whisperx-transcript-revised-50619/feed/</wfw:commentRss>
<slash:comments>1</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/04/856050467346607298.jpeg" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8274</post-id> </item>
<item>
<title>我的備份SOP (2025版)</title>
<link>https://blog.serv.idv.tw/2025/06/backup-sop-2025/</link>
<comments>https://blog.serv.idv.tw/2025/06/backup-sop-2025/#respond</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Mon, 16 Jun 2025 09:05:00 +0000</pubDate>
<category><![CDATA[電腦.網路]]></category>
<category><![CDATA[backblaze]]></category>
<category><![CDATA[backup]]></category>
<category><![CDATA[sop]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8266</guid>
<description><![CDATA[數位時代下,個人與家庭資料的安全性與完整性日益重要。我在2018年寫過一版當時的備份SOP。7年後,我更新了目 ... <a title="我的備份SOP (2025版)" class="read-more" href="https://blog.serv.idv.tw/2025/06/backup-sop-2025/" aria-label="Read more about 我的備份SOP (2025版)">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>數位時代下,個人與家庭資料的安全性與完整性日益重要。我在<a href="https://blog.serv.idv.tw/2018/04/backup-sop-2018/">2018年</a>寫過一版當時的備份SOP。7年後,我更新了目前我所使用的,如何在多裝置、大資料量的家庭環境中,建立一套高效且可靠的備份SOP。如果你的環境比較簡單,可以用下面的簡易版本SOP作備份:</p>
<ol class="wp-block-list">
<li><strong>異質備份:以大容量外接硬碟作為資料備份與歸檔的主要媒介。</strong>
<ul class="wp-block-list">
<li>將所有日常工作文件、重要家庭照片及影像等「熱資料」定期備份至外接硬碟。</li>
<li>對於不再頻繁使用但具長期保存價值的「冷資料」(如已完成專案的原始檔、高解析度照片RAW檔),定期將其歸檔/搬移至大容量外接硬碟,以釋放主力設備空間。</li>
</ul>
</li>
<li><strong>異地防護:採用 <a href="https://www.backblaze.com/">Backblaze</a> 進行雲端遠端備份。</strong>
<ul class="wp-block-list">
<li>僅依賴本地實體儲存仍不足以抵禦區域性災害(如火災、水患或竊盜)。因此,建議利用如 <a href="https://www.backblaze.com/">Backblaze</a> 這類提供個人電腦無限儲存空間的雲端備份服務,作為異地備份解決方案。</li>
<li><strong>策略性運用:</strong> 對於儲存於外接硬碟中的歸檔資料,可規劃每半年將該外接硬碟連接至主力電腦數日。此舉將使 Backblaze 偵測到並自動將這些歸檔資料納入遠端備份範圍,以極低的邊際成本實現冷資料的異地防護。</li>
</ul>
</li>
</ol>
<p>如果你對完整版的備份SOP有興趣,想了解我的心路歷程,或是想知道我怎麼處理較複雜的架構,可以繼續看下去。</p>
<span id="more-8266"></span>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p></p>
<p>距離我上次在部落格<a href="https://blog.serv.idv.tw/2018/04/backup-sop-2018/">分享我的備份策略</a>,已經將近七年。回想2018年,我正好面對<a href="https://www.crashplan.com/">Crashplan</a>終止家用方案的影響,懷著嘗試與摸索的心情,逐步將資料轉移到 Google Drive 的 (無限) 空間中。當時,我測試了一種混合方式:對於「熱檔案」進行自動同步,而「冷檔案」則手動上傳。在那個依賴單一個人電腦、資料量尚可控的時期,這種做法似乎行之有效。</p>
<p>在科技快速發展的七年間,我的數位裝置和資料存放/流向已經有了巨大的變化。</p>
<p>過去幾乎所有的資料都會匯集到 PC 上,包括文件、資料、照片、影音。所以備份的話針對主力工作PC 進行備份即可。<br>現在則是分散在各個不同的數位裝置上。以相片為例,用單眼相機或運動相機拍的照片,如過去一樣匯集在PC,但手機拍的已經分散在各自的裝置和對應的cloud service (如 iCloud 跟 Google Photo)。再加上孩子使用的數位裝置。要備份的資料已經分散在各地。</p>
<p>而PC 也是,七年之後,電腦已經從一台單打獨鬥的PC,擴展成為一台主力工作機、一台孩子遊戲PC、一台MacBook Air,以及三台遠端Linux伺服器組成的「聯合艦隊」。資料量伴隨著兩個孩子的成長、攝影和4K影音的成長,以及歲月的累積,已經成長到TB 這種等級的量。</p>
<p>還好,我對「資料保存」的思考已由過去的「都備份就對了」逐漸演變為更加細緻的見解:並非所有資料都生而平等。有些是攸關身家性命的每日必需品,而有些則是充滿回憶卻鮮少翻閱的傳家寶。將這些資料統一以同一標準對待,效率低下,同時也是資源浪費。</p>
<p>於是在2025年初夏,我再次進行了一次全面的備份體系盤點與改革。這篇文章便是我對現行策略的完整紀錄與思考沉澱。希望它不僅能成為未來自己的查核清單,也能為同樣在數位洪流中,為資料安身立命而苦惱的朋友們,提供一些可能的靈感。</p>
<p></p>
<h2 class="wp-block-heading">核心理念的演進:思考「備份」與「歸檔」</h2>
<p>過去,我對資料的態度相對單純:重要的資料就需要備份。然而,時間是最好的過濾器,它能改變資料的「溫度」。例如,半年前精心處理的專案文件、數年前的攝影作品RAW檔,以及剪輯完畢但佔據巨大空間的影片素材,這些資料在創作當下是「熱資料」,但隨著時間推移,這些資料的價值則轉變為「冷資料」,是「或許未來某天會用到,沒事的話就放著,不要輕易刪除」的類型。</p>
<p>如果將這些低頻率取用、高容量佔用的「冷資料」與日常文件、系統設定等高頻變動的「熱資料」混為一談,並用同樣的方式進行備份,將會面臨一系列棘手問題:日常備份速度將受到嚴重阻礙、主力電腦的儲存空間會迅速被侵蝕,備份成本也隨之上升。</p>
<p>因此,這個版本的策略核心在於將資料的生命週期管理劃分為兩大體系:</p>
<ol class="wp-block-list">
<li><strong>正規備份(Backup):</strong> 此類資料直接關係到我的日常工作與數位生活,一旦遺失會立即造成嚴重影響,因此我必須在最短時間內恢復。它們需要最嚴格、最可靠的保護。對此,我遵循並嚴格執行業界的 <strong>3-2-1 原則</strong>:
<ul class="wp-block-list">
<li><strong>三份備份:</strong> 原始檔案外,至少擁有兩份複本。</li>
<li><strong>兩種不同的儲存媒體:</strong> 一份在內置硬碟,另一份在外接硬碟或其他電腦上。</li>
<li><strong>一份是異地備份:</strong> 將一份複本存放於與本地不同的地理位置。</li>
</ul>
</li>
<li><strong>儲存歸檔(Archive):</strong> 此類資料的核心目標是「安全地移出主力工作機以釋放空間」及「實現低成本的長期保存」。通常來說,它們是已完成專案的原始檔、照片的RAW檔、影片的母帶等資料。即使這些檔案不幸遺失,雖然令人遺憾,但也只是「遺憾」,不會對當前的生活造成巨大的衝擊。歸檔的目標是保留至少1份資料在外部媒體,不追求同步或3-2-1備份,而是以週期性的方式整理與封存。如果要把資料取回,我可以接受較長的恢復時間,例如數週。</li>
</ol>
<p>在過去,我曾使用過所謂的「深儲存歸檔(Deep Archive)」,像是 Amazon的 S3 Glacier。但個人使用,長期存放的支出仍然可觀。2018使用的 google drive,在後來google修改政策後,可用容量大幅減少。後來我也試過像 Microsoft E5 類型的方案,希望能找到近乎零成本的雲端空間來存放那些「此生大概不會再看第二眼但又捨不得刪除」的陳年舊檔。不過,目前這些穩定可靠但又零成本方案已不復存在。因此,我將這個想法擱置,專注於將「備份」與「歸檔」兩個核心體系做到極致。</p>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p></p>
<h2 class="wp-block-heading">正規備份:我的3-2-1法則實踐方案</h2>
<p>針對我歸類在「正規備份」的資料——例如進行中的專案文件、重要的個人和家庭檔案、Lightroom的編目檔等——我以嚴謹的方式實施3-2-1法則:</p>
<ul class="wp-block-list">
<li><strong>第1份(本機備份資料):</strong> 資料的第一份備份檔案存放在本機的備份目錄裡 (過去是本機某個備份硬碟),例如<code>/backup</code>。然後用每月執行的腳本簡化備份流程的操作,只要一鍵就可以把需要備份的資料通通備份集合到備份目錄中。</li>
<li><strong>第2份(本地異質媒體備份):</strong> 資料的第二份備份存放在本地的第二台PC,那是一台從主力工作機退役,目前作為孩子遊戲或上網的機器。我在上面掛了一顆之前用來備份的硬碟,可以用來作為備份機。並且用腳本定期把主力工作機的 /backup 內容同步到第二台PC 上。因為資料同時存在於兩台物理獨立的電腦硬碟中。這樣子就滿足了「兩種不同媒體」的要求。即便主力工作機遭遇不測(如硬碟損毀),我依然能在備份機上找到一份相對完整的近期備份。</li>
<li><strong>第3份(異地遠端備份):</strong> 這是抵禦火災、地震等區域性災難的最後防線。在這方面,我使用<a href="https://www.backblaze.com/">Backblaze</a>這個服務,用兩年大概5000元的價格,進行遠端備份的服務。它提供了個人電腦無限儲存空間的服務。安裝客戶端後,它會在系統背景以極低的資源佔用,自動上傳硬碟上我所指定的資料。當有需要回復時,可以把要回復的資料下載回來;如果是大量的資料要回復 (TB 等級的),他們也提供直接把資料裝進硬碟裡,然後快遞到府的服務。這個會比我花數週時間下載來得迅速。</li>
</ul>
<p>這個組合拳——本機 + 本地異質 + Backblaze(遠端備份)——構建了一個即時性的、本地恢復速度快且具遠端災備能力的防護網,確保我的核心數位資產安全無虞。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="576" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/289_1x_shots_so.png?resize=1024%2C576&ssl=1" alt="" class="wp-image-8269" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/289_1x_shots_so.png?resize=1024%2C576&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/289_1x_shots_so.png?resize=400%2C225&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/289_1x_shots_so.png?resize=1536%2C864&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/289_1x_shots_so.png?w=1920&ssl=1 1920w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure></div>
<h2 class="wp-block-heading">儲存歸檔:資料的生命週期與封存藝術</h2>
<p>如果說「正規備份」是為了應對「意外」,那麼「儲存歸檔」就是一門管理數位遺產的藝術。它的處理流程更具週期性與儀式感,目標是在確保安全的同時,將陳年資料優雅地移出主力工作機。同時,也是一種資料斷捨離。(但不是真的捨棄)</p>
<p>以占用空間最顯著的「照片RAW檔」為例,我的歸檔流程同時也展現了資料由熱到冷的生命階段:</p>
<ol class="wp-block-list">
<li><strong>誕生與處理(在工作機):</strong> 完成攝影專案後,RAW檔會進入工作機的對應目錄。在使用JPG檔編修的同時,如果出現不足,就會找出對應的RAW 進行編修與輸出。在這階段,它們是站在JPG 檔後面的第二道防線。</li>
<li><strong>暫時存放(在工作機):</strong> 照片處理完後,JPG成品輸出,這些佔據大量空間的RAW檔任務便完成。我會將它們整批移至工作機歸檔專用目錄<code>/Archives</code>,並依年份、專案分類。</li>
<li><strong>首次歸檔(工作機 -> 外接硬碟 + 備份機,半年一次):</strong> 每年6月和12月,我會執行一次大規模的歸檔,將需要歸檔的資料移動到大容量外接硬碟。如果備份機有空間的話,我也會放置一份副本,作為歸檔資料的備份 (但非必要)。</li>
<li><strong>遠端歸檔(外接硬碟 -> Backblaze,半年一次):</strong> 這是我的歸檔策略中最特殊的環節。因為對大容量外接硬碟的可靠度不是完全放心,所以把資料移動到外接硬碟後,我會將大容量外接硬碟接在主力機上一陣子。讓Backblaze偵測到這些外接硬碟。由於我已經把Backblaze 的資料過期期限提升至1年,所以其實Backblaze 的雲端已經有半年前我上傳的資料。他只需要重新確認資料存在,就可以繼續保存下去。透過這樣看似繞路的操作,我可以用極低的邊際成本實現了歸檔資料的遠端備份。而不需要讓那些歸檔資料佔用主力機的空間,我只需每半年將外接硬碟接在主力機上幾天,確保Backblaze 確認資料的存在,並且繼續保存下去。這是一種非典型的「冷備份」上雲方式,兼顧了成本與效益。至於新歸檔的資料,就要多放幾天,讓backblaze 完成上傳備份的任務。</li>
</ol>
<p></p>
<h2 class="wp-block-heading">特殊資料類型的備份策略:處理那些「例外」</h2>
<p>在「備份」和「歸檔」的兩大主軸之外,還總有一些「例外」,需要特別處理:</p>
<ul class="wp-block-list">
<li><strong>iCloud照片:</strong> 手機是現代人生活紀錄的核心工具,iOS 的照片我會交給 iCloud 來儲存。但 iCloud 與其說備份,不如說是一個同步的雲端。備份的話,我目前是選擇開源工具<a href="https://github.com/icloud-photos-downloader/icloud_photos_downloader">icloudpd</a>。設定每月執行一次增量備份,把 iCloud 照片下載存到工作機的某個資料夾,同樣受到Backblaze的遠端備份保護。</li>
<li><strong>MacBook資料:</strong> Macbook Air 對我而言,比較像是在外的連網裝置。大部份的資料都在雲端,所以我僅僅使用了 Time Machine 作不定期的備份,將系統備份至一顆1TB的外接SSD。</li>
<li><strong>Google Photo:</strong> 照片的部份除了 Apple 生態系的 iCloud 外,我也同步放了一份在 Google Photo,同時還加了許多從其他來源上傳的照片。這部份比較像是過去備份機制產生的legacy。但多一份資料,也無傷大雅 (只是沒有整理)。Google Photo 的部份需要購買 Google one 來解決容量不足的問題,另外我也每五年用 google takeout 取出一份,歸檔處理。</li>
<li><strong>遠端伺服器:</strong> 至於遠端Linux的主機,承載著我的個人網站及一些實驗性服務。我分別為它們寫了備份腳本,可以將網站文件、資料庫及Nginx設定檔打包壓縮。然後再定期用<code>rclone</code> 掛載 Google Drive,手動將備份檔移上雲端。雖然這部分的手動操作不如全自動流程優雅,但它強迫我每個月親自檢查一次伺服器,也算是一種有益的例行檢查。</li>
</ul>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>小叮嚀:</strong></p>
<p>如果要用 rclone mount 把 google drive 掛上來讀寫時,請記得把每個要上傳的檔案大小控制在 2GB 以下 (我是用split 切成1GB)。</p>
<p>另外vfs 請愛用 <code>rclone --vfs-cache-mode minimal</code> ,這樣比較不會被 google block/reject。</p>
</blockquote>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p></p>
<h2 class="wp-block-heading">結語:一場永不結束的數位長征</h2>
<p>備份從來不是一個可以一勞永逸的專案,它更像一場無止境的數位長征,需持續關注、定期檢視,並隨時準備調整方向。整理自己的數位空間,雖然過程繁瑣,但當你環顧那個井然有序、安全無憂的結果,那份油然而生的成就感,正是對所有努力的最佳報答。</p>
<p>沒有任何一個備份策略是完美的,隨著時間推移必然會暴露出新的問題。目前的架構雖然穩固,不過跟7年前最大的不同就是…用錢解決。添購大容量的硬碟、Backblaze 的訂閱、google One 的訂閱等等,都是一筆開銷。但我都跟自己說:這不是買硬體,這是買保險,避免未來的後悔。</p>
<p>從2018年那個將所有希望寄託於Google Drive的單純年代,到2025年這個「備份、歸檔分流,異質/異地備份 + Backblaze遠端」的複雜協同備份體系,我的備份策略無疑變得更加繁複 (還好有腳本),但也因此更為穩固與靈活。</p>
<p>這份落落長的備份SOP,其實就是我這七年數位生活變化的縮影。 一方面藉由分辨「備份」跟「歸檔」,把數位生活裡累積的巨量資料作初步的斷捨離,一方面也更嚴謹地看待所謂的3-2-1法則。希望我的這些想法,能給同樣在數位汪洋中掙扎的你一點點靈感,讓你也能打造一套專屬自己的備份體系,從此告別資料焦慮,把心力花在更重要的事情上。</p>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/06/backup-sop-2025/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2016/02/20607701226_e0b1226cd2_b.jpg" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8266</post-id> </item>
<item>
<title>n8n Workflow: 在Karakeep 打星就分享到 Twitter</title>
<link>https://blog.serv.idv.tw/2025/06/n8n-workflow-karakeep/</link>
<comments>https://blog.serv.idv.tw/2025/06/n8n-workflow-karakeep/#respond</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Mon, 09 Jun 2025 09:05:00 +0000</pubDate>
<category><![CDATA[電腦.網路]]></category>
<category><![CDATA[IFTTT]]></category>
<category><![CDATA[Karakeep]]></category>
<category><![CDATA[n8n]]></category>
<category><![CDATA[推文]]></category>
<category><![CDATA[自動化]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8249</guid>
<description><![CDATA[上週從Pocket跳船到 Karakeep 之後,由於 IFTTT 不支援 Karakeep,之前 IFTTT ... <a title="n8n Workflow: 在Karakeep 打星就分享到 Twitter" class="read-more" href="https://blog.serv.idv.tw/2025/06/n8n-workflow-karakeep/" aria-label="Read more about n8n Workflow: 在Karakeep 打星就分享到 Twitter">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>上週<a href="https://blog.serv.idv.tw/2025/06/from-pocket-to-karakeep/">從Pocket跳船到 Karakeep</a> 之後,由於 IFTTT 不支援 Karakeep,之前 IFTTT 上的「打星就分享到 twitter流程」就沒得用了。</p>
<p>正好也想用 n8n 玩點花樣,於是就到 <a href="https://n8n.io/">n8n</a> 上試著寫個 workflow 來做這件事。</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>個人血淚提醒:</strong><br>使用 docker compose down 時千萬不要加 “- v” (也就是不要下 docker compose down -v)。<br>一般來說 -v 會讓人想到 –verbose;但在 docker compose 裡是把已經建好的 volume 移除 (remove),包含之前所有輸入的資料,workflow,以及設定。</p>
<p>我寫到一半要加個功能,想要rebuild container時,粗心大意直接 copy & paste chatGPT 給的指令,然後兩天的心血就…消失了。後面問 chatGPT 他還理直氣壯說我又沒有說要保存 volume 資料… Orz</p>
</blockquote>
<p>以下的內容是先請 AI 分析我寫的 workflow,然後我再補充。<br>這樣產生說明文件的方式真的很快。不過某些我覺得重要的節點(node)還是會被略過。得要手動指定或是手動加入。</p>
<p></p>
<p>workflow 拆成兩個部份:</p>
<ul class="wp-block-list">
<li><strong>Karakeep_webhook_queue.json</strong>:接收 Karakeep 更新書籤時所送出的 webhook,稍後由 Karakeep_share 批次處理。</li>
<li><strong>Karakeep_share.json</strong>:把已經打星的書籤分享到 Twitter,並把已經分享過的書籤歸到 “shared” 列表中。</li>
</ul>
<p>我請AI 從功能、架構與流程、重要節點的設定方式等三個面向進行分析。</p>
<span id="more-8249"></span>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">1. Karakeep_webhook_queue.json</h2>
<figure class="wp-block-image size-large is-style-default"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="576" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/856_1x_shots_so.png?resize=1024%2C576&ssl=1" alt="Karakeep_webhook_queue.json" class="wp-image-8250" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/856_1x_shots_so.png?resize=1024%2C576&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/856_1x_shots_so.png?resize=400%2C225&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/856_1x_shots_so.png?resize=1536%2C864&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/856_1x_shots_so.png?w=1920&ssl=1 1920w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<h3 class="wp-block-heading">功能</h3>
<p><code>Karakeep_webhook_queue.json</code> 是一個專為處理 Karakeep webhook 事件設計的 workflow。其主要目的是接收來自 Karakeep 的 webhook 請求(例如書籤更新事件),將相關數據(如 <code>jobId</code> 和 <code>bookmarkId</code>)緩存到文件中,並在一定時間後觸發另一個 workflow 進行批次處理。這種設計特別適用於應對可能短時間內大量觸發的 webhook 事件,通過緩存避免過載並實現批次處理。</p>
<h3 class="wp-block-heading">架構與流程簡介</h3>
<p>這個 workflow 的架構由以下核心節點組成:</p>
<ul class="wp-block-list">
<li><strong>Webhook 節點</strong>:作為入口,接收 POST 請求。</li>
<li><strong>Set 節點</strong>:從請求中提取關鍵數據並格式化。</li>
<li><strong>Execute Command 節點</strong>:用於文件操作,包括寫入隊列 (queue) 和設置獨占標誌。</li>
<li><strong>IF 節點</strong>:判斷是否成為處理者。</li>
<li><strong>Wait 節點</strong>:延遲執行以收集更多事件。</li>
<li><strong>Execute Workflow 節點</strong>:觸發後續處理。</li>
</ul>
<p><strong>流程如下</strong>:</p>
<ol class="wp-block-list">
<li>Webhook 節點接收 Karakeep 的 POST 請求。</li>
<li>Set 節點提取 <code>jobId</code> 和 <code>bookmarkId</code>,生成 JSON 對象。</li>
<li>Execute Command 節點將數據追加到 <code>/files/karakeep-webhook-queue.txt</code>,使用 <code>flock</code> 避免同時寫入的競爭條件 (race condition)。</li>
<li>另一個 Execute Command 節點嘗試設置獨占標誌 <code>/files/karakeep-process.flag</code>,檢查是否已有處理者在運行。</li>
<li>如果成為處理者,Wait 節點等待 10 秒,收集更多 webhook 事件 (當在 Karakeep 使用批次處理時)。</li>
<li>Execute Workflow 節點觸發 “<code>Karakeep_share</code>” 這個 workflow,進行後續的處理。</li>
<li>最後清除隊列文件和獨占標誌。</li>
</ol>
<p></p>
<h3 class="wp-block-heading">重要節點的設定方式</h3>
<p>以下說明各重要節點在 n8n 中的具體設定方式:</p>
<ul class="wp-block-list">
<li><strong>Webhook 節點</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.webhook</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 n8n 介面中添加 Webhook 節點。</li>
<li>設置 <strong>HTTP Method</strong> 為 <code>POST</code>,用於接收 Karakeep 的 webhook 請求。</li>
<li>設置 <strong>Path</strong> 為 <code>karakeep-bookmark-update-XXXX</code>,作為此 workflow 的唯一路徑。</li>
<li>在 <strong>Authentication</strong> 中選擇 <code>Header Auth</code>,並配置自定義憑證(例如 API 密鑰),確保安全性。這裡的 key 要跟 Karakeep 那邊的 webhook 觸發設定一致。</li>
</ol>
</li>
<li><strong>用途</strong>:此節點作為 workflow 的觸發點,接收 Karakeep 請求並傳遞數據給後續節點。</li>
</ul>
</li>
<li><strong>Set 節點</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.set</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>添加 Set 節點並選擇 <strong>Mode</strong> 為 JSON,以手動定義輸出數據。</li>
<li>在 <strong>JSON Output</strong> 欄位中輸入表達式,例如: <br><code>{"jobid": "{{ $json.body.jobId }}", "bookmarkid": "{{ $json.body.bookmarkId }}", "time": {{ $now }}, "log_str": "{{ $now.toISO() }} - {{ $json.body.jobId }} - {{ $json.body.bookmarkId }}"}</code></li>
<li>確保表達式正確引用上游 Webhook 節點的輸入數據(如 <code>body.jobId</code>)。</li>
</ol>
</li>
<li><strong>用途</strong>:用於從 webhook 請求中提取並格式化關鍵數據,生成結構化的 JSON 輸出。</li>
</ul>
</li>
<li><strong>Execute Command 節點(寫入隊列)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.executeCommand</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Command</strong> 欄位輸入以下 shell 命令: <br><code>echo "{{ $json.log_str }}" | flock -x /files/karakeep-webhook-queue.txt tee -a /files/karakeep-webhook-queue.txt</code></li>
<li>確保命令中使用 <code>$json.log_str</code> 引用 Set 節點生成的日誌字符串。</li>
</ol>
</li>
<li><strong>用途</strong>:通過 <code>flock</code> 實現文件鎖定,將資料地安全追加到隊列文件中,避免同時寫入問題。</li>
</ul>
</li>
<li><strong>Execute Command 節點(設立獨占標誌)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.executeCommand</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Command</strong> 欄位輸入包含檔案檢查與鎖定邏輯的腳本,例如檢查檔案是否存在、過期處理(10 分鐘)並使用 <code>flock</code> 設置標誌。</li>
<li>確保腳本輸出 “Success” 或其他標識,以便後續 IF 節點判斷。</li>
</ol>
</li>
<li><strong>用途</strong>:管理獨占標誌,確保同一時間只有一個 workflow 實例處理隊列。</li>
</ul>
</li>
<li><strong>IF 節點</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.if</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Conditions</strong> 中添加一個條件:
<ul class="wp-block-list">
<li>選擇 <code>$json.stdout</code>(前一節點的輸出)。</li>
<li>設置操作為 <code>Contains</code>,值為 <code>"Success"</code>。</li>
</ul>
</li>
<li>配置 True 和 False 兩條分支,分別連接後續節點。</li>
</ol>
</li>
<li><strong>用途</strong>:根據獨占標誌設置的結果,決定是否繼續執行處理邏輯。</li>
</ul>
</li>
<li><strong>Wait 節點</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.wait</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Amount</strong> 欄位設置為 <code>10</code>,單位為 <code>Seconds</code>。</li>
</ol>
</li>
<li><strong>用途</strong>:暫停 10 秒後再繼續,允許收集更多 webhook 事件以進行批次處理。</li>
</ul>
</li>
<li><strong>Execute Workflow 節點</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.executeWorkflow</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Workflow ID</strong> 欄位選擇目標 workflow 的 ID,例如 (對應 <code>Karakeep_share</code>)。</li>
<li>啟用 <strong>Wait for Sub-Workflow</strong> 選項,設為 <code>true</code>,確保子流程完成後再繼續。</li>
</ol>
</li>
<li><strong>用途</strong>:觸發另一個 workflow 來處理緩存的隊列數據。</li>
</ul>
</li>
</ul>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">2. Karakeep_share.json</h2>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="576" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/305_1x_shots_so.png?resize=1024%2C576&ssl=1" alt="Karakeep_share.json" class="wp-image-8252" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/305_1x_shots_so.png?resize=1024%2C576&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/305_1x_shots_so.png?resize=400%2C225&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/305_1x_shots_so.png?resize=1536%2C864&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/06/305_1x_shots_so.png?w=1920&ssl=1 1920w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<h3 class="wp-block-heading">功能</h3>
<p><code>Karakeep_share.json</code> 的目標是從 Karakeep 獲取標記為收藏的書籤,過濾出鏈接類型書籤,根據其內容(note 或 highlight)生成 Twitter 推文內容,然後通過 IFTTT 的webhook發送推文 (這裡我偷懶了 XD),最後將書籤移動到 “shared” 列表。 (記得先在 Karakeep 建好 shared 這個list, 大小寫有分)</p>
<h3 class="wp-block-heading">架構與流程簡介</h3>
<p>這個 workflow 的架構包括:</p>
<ul class="wp-block-list">
<li><strong>Manual Trigger / Execute Workflow Trigger 節點</strong>:觸發點。</li>
<li><strong>HTTP Request 節點</strong>:與 Karakeep API 交互。</li>
<li><strong>Split Out / Filter 節點</strong>:處理書籤數據。</li>
<li><strong>Set / IF / Code 節點</strong>:生成推文。</li>
<li><strong>Merge 節點</strong>:合併數據流。</li>
<li><strong>HTTP Request 節點</strong>:把推文送到 IFTTT 。</li>
</ul>
<p><strong>流程如下</strong>:</p>
<ol class="wp-block-list">
<li>觸發 workflow。</li>
<li>HTTP Request 節點查詢收藏書籤。</li>
<li>Split Out 節點拆分書籤列表。</li>
<li>Filter 節點篩選鏈接類型書籤。</li>
<li>Set 節點提取關鍵資料。</li>
<li>IF 節點檢查是否有 <code>note</code>。</li>
<li>如果沒有 note,檢查是否有 highlight。若有,取得 highlight 內容。</li>
<li>如果連 highlight 也沒有,那就是最一般的情況。</li>
<li>用 Code 節點生成推文。</li>
<li>用 Merge 節點合併結果。</li>
<li>移動書籤並發送推文。</li>
</ol>
<h3 class="wp-block-heading">重要節點的設定方式</h3>
<p>以下說明各重要節點在 n8n 中的具體設定方式:</p>
<ul class="wp-block-list">
<li><strong>HTTP Request 節點(查詢書籤)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.httpRequest</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>設置 <strong>URL</strong> 為 <code>https://[karakeep site]/api/v1/bookmarks/search</code>。</li>
<li>在 <strong>Authentication</strong> 中選擇 <code>Generic Credential Type</code>,並配置 <code>Bearer Auth</code> 憑證。 這裡的 API是要存取 Karakeep API 用的。需要在 Karakeep 的 user -> API 那邊建立並取得 API key。</li>
<li>在 <strong>Query Parameters</strong> 中添加:
<ul class="wp-block-list">
<li><code>q=is:fav -list:shared -list:Personal</code></li>
<li><code>includeContent=false</code></li>
<li><code>limit=20</code></li>
</ul>
</li>
</ol>
</li>
<li><strong>用途</strong>:從 Karakeep API 獲取符合條件的書籤數據。</li>
</ul>
</li>
<li><strong>Filter 節點</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.filter</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Conditions</strong> 中設置條件:
<ul class="wp-block-list">
<li><code>$json.content.type</code> 等於 <code>"link"</code>。</li>
</ul>
</li>
</ol>
</li>
<li><strong>用途</strong>:篩選出鏈接類型的書籤,過濾掉其他類型。</li>
</ul>
</li>
<li><strong>Set 節點</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.set</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Assignments</strong> 中定義多個字段,例如:
<ul class="wp-block-list">
<li><code>id</code>:<code>{{ $json.id }}</code></li>
<li><code>note</code>:<code>{{ $json.note }}</code></li>
<li><code>content.url</code>:<code>{{ $json.content.url }}</code></li>
</ul>
</li>
</ol>
</li>
<li><strong>用途</strong>:提取要分享的書籤內容資料。</li>
</ul>
</li>
<li><strong>IF 節點(檢查 note)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.if</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Conditions</strong> 中設置:
<ul class="wp-block-list">
<li><code>$json.note</code> 不為空(<code>Not Empty</code>)。</li>
</ul>
</li>
<li>配置 True 和 False 分支。</li>
</ol>
</li>
<li><strong>用途</strong>:判斷書籤是否有 note,決定推文生成方式。</li>
</ul>
</li>
<li><strong>HTTP Request 節點(取得Highlight)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.httpRequest</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>設置 <strong>URL</strong> 為 <code>https://[karakeep site]/api/v1/bookmarks/{{ $json.id }}/highlights。</code></li>
<li>在 <strong>Authentication</strong> 中選擇 <code>Generic Credential Type</code>,並配置 <code>Bearer Auth</code> 憑證。</li>
<li>在 <strong>Query Parameters</strong> 中添加:
<ul class="wp-block-list">
<li><code>bookmarkId={{ $json.id }}</code></li>
</ul>
</li>
</ol>
</li>
<li><strong>用途</strong>:從 Karakeep API 獲取符合條件的書籤數據。</li>
</ul>
</li>
<li><strong>IF 節點(檢查 highlight)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.if</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>在 <strong>Conditions</strong> 中設置:
<ul class="wp-block-list">
<li><code>{{ $json.highlights[0].bookmarkId }}</code> 不為空(<code>Not Empty</code>)。</li>
</ul>
</li>
<li>配置 True 和 False 分支。</li>
</ol>
</li>
<li><strong>用途</strong>:判斷書籤是否有 highlight,決定推文生成方式。</li>
</ul>
</li>
<li><strong>Code 節點(生成推文)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.code</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>添加 Code 節點並設置 <strong>Mode</strong> 為 <code>Run Once for Each Item</code>。</li>
<li>在 <strong>JS Code</strong> 欄位輸入自定義 JavaScript,例如計算字數、裁剪內容並生成推文。</li>
</ol>
</li>
<li><strong>用途</strong>:處理數據並生成符合 Twitter 要求的推文內容。</li>
</ul>
</li>
</ul>
<p>以下是我用來產生推文的程式碼,內容有點雜亂,供參考參考。</p>
<p><br><strong>for note:</strong></p>
<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code=" // 計算字數的函式
function countTwitterLength(text) {
const URL_LENGTH = 23;
let length = 0;
for (let char of text) {
length += char.charCodeAt(0) <= 0x7f ? 1 : 2;
}
return length;
}
const URL_LENGTH = 23;
const maxLen = 280;
const ending = '…';
// note 推文格式: Note (無引號) [讀] 標題 網址
// 計算標題長度
const tweet_title = '[讀] ' + $json.content.title;
const tweet_title_length = countTwitterLength(tweet_title);
$json.tweet_title = tweet_title;
$json.tweet_title_length = tweet_title_length;
const max_note_length = maxLen - URL_LENGTH - tweet_title_length - 3 - 3; // ... + 換行
// 裁剪 note 部份
let result = '';
let length = 0;
const tweet_note_orig = $json.note;
const tweet_note_orig_length = countTwitterLength(tweet_note_orig);
for (let i = 0; i < tweet_note_orig.length; i++) {
const char = tweet_note_orig[i];
const charLen = char.charCodeAt(0) <= 0x7f ? 1 : 2;
// 若加上這個字已經超過 maxLen,就停止,並補尾碼
if (length + charLen > max_note_length) {
result += ending;
break;
}
result += char;
length += charLen;
}
$json.tweet_note = result;
// 加回 標題 和 網址(每個網址算固定 23 字元)
$json.tweet_msg = $json.tweet_note + "\n\n" + $json.tweet_title + "\n" + $json.content.url;
return $json;" style="color:#d8dee9ff;display:none" aria-label="複製" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4"> </span><span style="color: #616E88">// 計算字數的函式</span></span>
<span class="line"><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">countTwitterLength</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">text</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">URL_LENGTH</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">23</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">of</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">text</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">charCodeAt</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1"><=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x7f</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">URL_LENGTH</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">23</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">maxLen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">280</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ending</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">…</span><span style="color: #ECEFF4">'</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// note 推文格式: Note (無引號) [讀] 標題 網址</span></span>
<span class="line"><span style="color: #616E88">// 計算標題長度</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">[讀] </span><span style="color: #ECEFF4">'</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">content</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">title</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">countTwitterLength</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tweet_title</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_title_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title_length</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">max_note_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">maxLen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">URL_LENGTH</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// ... + 換行</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 裁剪 note 部份 </span></span>
<span class="line"><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">''</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_note_orig</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">note</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_note_orig_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">countTwitterLength</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tweet_note_orig</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1"><</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_note_orig</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_note_orig</span><span style="color: #D8DEE9FF">[</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">]</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">charLen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">charCodeAt</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1"><=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x7f</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4"> </span><span style="color: #616E88">// 若加上這個字已經超過 maxLen,就停止,並補尾碼</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">charLen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">></span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">max_note_length</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ending</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">break;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">charLen</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_note</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 加回 標題 和 網址(每個網址算固定 23 字元)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_msg</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_note</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #EBCB8B">\n\n</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">content</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">url</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #81A1C1">;</span></span></code></pre></div>
<p></p>
<p><strong>for hightlight:</strong></p>
<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="// 計算字數的函式
function countTwitterLength(text) {
const URL_LENGTH = 23;
let length = 0;
for (let char of text) {
length += char.charCodeAt(0) <= 0x7f ? 1 : 2;
}
return length;
}
const URL_LENGTH = 23;
const maxLen = 280;
const ending = '…';
// note 推文格式: Note (無引號) [讀] 標題 網址
// 計算標題長度
const tweet_title = '[讀] ' + $json.content.title;
const tweet_title_length = countTwitterLength(tweet_title);
$json.tweet_title = tweet_title;
$json.tweet_title_length = tweet_title_length;
const max_note_length = maxLen - URL_LENGTH - tweet_title_length - 3 - 3 - 2; // ... + 換行 + 引號
// 裁剪 highlight 部份
let result = '';
let length = 0;
const tweet_note_orig = $json.highlights[0].text
const tweet_note_orig_length = countTwitterLength(tweet_note_orig);
for (let i = 0; i < tweet_note_orig.length; i++) {
const char = tweet_note_orig[i];
const charLen = char.charCodeAt(0) <= 0x7f ? 1 : 2;
// 若加上這個字已經超過 maxLen,就停止,並補尾碼
if (length + charLen > max_note_length) {
result += ending;
break;
}
result += char;
length += charLen;
}
$json.tweet_highlight = result;
// 加回 標題 和 網址(每個網址算固定 23 字元)
$json.tweet_msg = '"' + $json.tweet_highlight + '"' + "\n\n" + $json.tweet_title + "\n" + $json.content.url;
return $json;" style="color:#d8dee9ff;display:none" aria-label="複製" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// 計算字數的函式</span></span>
<span class="line"><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">countTwitterLength</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">text</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">URL_LENGTH</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">23</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">of</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">text</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">charCodeAt</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1"><=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x7f</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">URL_LENGTH</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">23</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">maxLen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">280</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ending</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">…</span><span style="color: #ECEFF4">'</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// note 推文格式: Note (無引號) [讀] 標題 網址</span></span>
<span class="line"><span style="color: #616E88">// 計算標題長度</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">[讀] </span><span style="color: #ECEFF4">'</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">content</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">title</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">countTwitterLength</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tweet_title</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_title_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title_length</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">max_note_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">maxLen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">URL_LENGTH</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_title_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// ... + 換行 + 引號</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 裁剪 highlight 部份 </span></span>
<span class="line"><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">''</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_note_orig</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">highlights</span><span style="color: #D8DEE9FF">[</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">text</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_note_orig_length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">countTwitterLength</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tweet_note_orig</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1"><</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_note_orig</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tweet_note_orig</span><span style="color: #D8DEE9FF">[</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">]</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">charLen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">charCodeAt</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1"><=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x7f</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4"> </span><span style="color: #616E88">// 若加上這個字已經超過 maxLen,就停止,並補尾碼</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">charLen</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">></span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">max_note_length</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ending</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">break;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">length</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">charLen</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_highlight</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">result</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 加回 標題 和 網址(每個網址算固定 23 字元)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_msg</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">'</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_highlight</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">"</span><span style="color: #ECEFF4">'</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #EBCB8B">\n\n</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tweet_title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">content</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">url</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$json</span><span style="color: #81A1C1">;</span></span></code></pre></div>
<ul class="wp-block-list">
<li><strong>Merge 節點</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:<code>n8n-nodes-base.merge</code></li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>添加 Merge 節點。</li>
<li>設置 <strong>Mode</strong> 為 <code>Combine</code>,並選擇 <strong>Combine By</strong> 為 <code>Position</code>。</li>
</ol>
</li>
<li><strong>用途</strong>:合併來自不同分支的數據流,準備後續處理。</li>
</ul>
</li>
</ul>
<ul class="wp-block-list">
<li><strong>HTTP Request 節點(get lists)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:n8n-nodes-base.httpRequest</li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>設置 <strong>URL</strong> 為 https://[karakeep site]/api/v1/lists。</li>
<li>在 <strong>Authentication</strong> 中選擇 Generic Credential Type,並配置 Bearer Auth 憑證。</li>
<li>啟用 <strong>Execute Once</strong> 選項設為 true,以避免對每條書籤數據重複請求。我只是要取得 “shared” 這個 list 的 id 而已…</li>
<li>可選:在 <strong>Response Format</strong> 中選擇 JSON,確保返回數據易於解析。</li>
</ol>
</li>
<li><strong>用途</strong>:從 Karakeep API 獲取所有列表的數據,包括 “shared” 列表的 ID,用於後續將書籤移動到指定列表。</li>
</ul>
</li>
<li><strong>HTTP Request 節點(send to buffer via IFTTT)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:n8n-nodes-base.httpRequest</li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>設置 <strong>Method</strong> 為 POST。</li>
<li>設置 <strong>URL</strong> 為 IFTTT 的 webhook URL,例如 https://maker.ifttt.com/trigger/[IFTTT webhook trigger]/with/key/[IFTTT_webhook_key]。</li>
<li>在 <strong>Body Parameters</strong> 中添加:
<ul class="wp-block-list">
<li>名稱:value1,值:{{ $json.tweet_msg }}(引用生成的推文內容)。</li>
</ul>
</li>
<li>在 <strong>Options</strong> 中啟用 <strong>Send Body</strong> 並選擇 JSON 格式。</li>
</ol>
</li>
<li><strong>用途</strong>:通過 IFTTT 的 webhook 將生成的推文內容發送到 Buffer,進而在 Twitter 上發布。</li>
</ul>
</li>
<li><strong>HTTP Request 節點(move to shared list)</strong>
<ul class="wp-block-list">
<li><strong>節點類型</strong>:n8n-nodes-base.httpRequest</li>
<li><strong>設定步驟</strong>:
<ol class="wp-block-list">
<li>設置 <strong>Method</strong> 為 PUT。</li>
<li>設置 <strong>URL</strong> 為 https://[karakppe site]/api/v1/lists/{{ $json.list_id }}/bookmarks/{{ $json.id }},其中 list_id 從 get lists 節點獲取,id 為書籤 ID。</li>
<li>在 <strong>Authentication</strong> 中選擇 Generic Credential Type,並配置 Bearer Auth 憑證。</li>
<li>啟用 <strong>Never Error</strong> 選項設為 true,以避免因 API 錯誤中斷 workflow。</li>
</ol>
</li>
<li><strong>用途</strong>:將已分享的書籤移動到 “shared” 列表,標記其完成狀態。</li>
</ul>
</li>
</ul>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">Future Work:</h2>
<p>之後還有一些 future work 可以做,像是:</p>
<ul class="wp-block-list">
<li>不要透過 IFTTT 去發推文,直接從 n8n 去呼叫 twitter API。</li>
<li>在 n8n 中加上類似 <a href="https://buffer.com/">buffer.app</a> 定時發文的功能。</li>
<li>較長的文字不要裁剪,改用回覆的方式支援長文。</li>
<li>Thread 發文支援</li>
<li>…</li>
</ul>
<p></p>
<p>這些就以後有空再實作練習囉。</p>
<p></p>
<p></p>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/06/n8n-workflow-karakeep/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/06/871916494223592855.png" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8249</post-id> </item>
<item>
<title>從 pocket 跳船到 Karakeep</title>
<link>https://blog.serv.idv.tw/2025/06/from-pocket-to-karakeep/</link>
<comments>https://blog.serv.idv.tw/2025/06/from-pocket-to-karakeep/#comments</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Mon, 02 Jun 2025 09:05:00 +0000</pubDate>
<category><![CDATA[電腦.網路]]></category>
<category><![CDATA[GTD]]></category>
<category><![CDATA[IFTTT]]></category>
<category><![CDATA[Karakeep]]></category>
<category><![CDATA[Pocket]]></category>
<category><![CDATA[self-hosted]]></category>
<category><![CDATA[simplenote]]></category>
<category><![CDATA[網路服務]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8238</guid>
<description><![CDATA[從 gslin 那邊看到 Pocket 要收攤的消息:《 Pocket 總算要關掉了… 》Pocket 這種「 ... <a title="從 pocket 跳船到 Karakeep" class="read-more" href="https://blog.serv.idv.tw/2025/06/from-pocket-to-karakeep/" aria-label="Read more about 從 pocket 跳船到 Karakeep">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>從 gslin 那邊看到 Pocket 要收攤的消息:《 <a href="https://blog.gslin.org/archives/2025/05/23/12409/pocket-%e7%b8%bd%e7%ae%97%e8%a6%81%e9%97%9c%e6%8e%89%e4%ba%86/">Pocket 總算要關掉了…</a> 》<br>Pocket 這種「稍後閱讀 (Read it Later)」的工具,在我 GTD 的流程裡扮演了一個很重要的角色。<br>需要閱讀的東西,用方便的小工具 (早期是 bookmarklet,現在是 iOS的 share 跟 browser 的 extension) 丟進去 pool 裡,讀完的 archive 起來。 早期還會想要分類下 tag,但後來實在太麻煩,而且全文搜尋太好用,就不下 tag 或分類了。</p>
<p>我記得早期我是用 <a href="https://www.instapaper.com/">Instapaper</a> 的,(可能是) 2014年初轉到 <a href="https://getpocket.com">Pocket</a>。<br>當 Mozilla 買下 Pocket,我也沒什麼在意。頂多是後來帳號整合在一塊,登入的時候,要想一下用哪個密碼 (其實也不用記,有自動填入)。</p>
<p>之後就一直留在 Pocket 沒移動過。一直到 <a href="https://getpocket.com/farewell">Mozilla 決定關掉這個服務</a>為止,我才驚醒:哇!工作流程要被影響了!</p>
<p>即使現在有那麼多 AI powered,跟桌面程式深度整合的「資訊整合/閱讀」服務,我還是用著古老的 Pocket 跟 Simplenote。一個負責連結,一個負責文字、其他、和初步整理。深度的整理現在是在 Obsidian 上進行,閱讀後的發佈和分享則是交給 blog / twitter / thread 等。閱讀網路文章時,如果覺得適合分享,又不需要(或不適合)打太多心得,早期我用小海的 twitthat,後來就把 Pocket 打星的文章經過 IFTTT串到 twitter上。這麼做,已經很多年了。</p>
<p>但事情來了,就要面對。<br>於是這一週就花了兩天處理跳船的事情。<br>處理的同時,也在擔心 <a href="https://en.wikipedia.org/wiki/Simplenote">Simplenote</a> 的狀況:跟 Pocket 一樣,被大型服務 (Automattic,就是Wordpress的那家公司)併購後,Simplenote 的開發一直…處於放生的狀態。雖然說服務只要穩定就好,不一定要一直疊加功能上去,但 Simplenote 的狀況比 Pocket 更悲戚一些:沒有穩定的付費模式,<strong>本來開放的 API 陸續收回 (雖然某程度上還有辦法用),第三方的工具一直隕落,也一直缺乏和其他服務的串接 (沒有太多自動化的機會),也沒有針對AI 世代有什麼明顯的回應</strong>。</p>
<p>簡單來說,就是一個放著 run,燒不了太多錢,但也沒有花心力維護的服務。</p>
<p>但他還是我用過最「方便」,最順手,也留在我GTD 工具清單最久的工具之一。</p>
<p>這樣一個服務,要是某天 Automatic 把它關掉,我應該會哀嚎得更大聲。</p>
<p>好了,扯遠了。</p>
<p>回到 Pocket 跳船。</p>
<span id="more-8238"></span>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading" id="-96bc1a07-8896-4717-aca8-9e77b880a518">選擇</h2>
<p>在找了幾個相類似的服務,包括公開服務的網站 跟 self-hosted (自托管)的選項後。最後我決定先試 <a href="https://karakeep.app/">Karakeep</a> 這個可以自托管的服務,然後把工具架在自己的伺服器上。</p>
<p>以下是我評估過的服務:</p>
<p></p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<h3 class="wp-block-heading">公開服務</h3>
<ul class="wp-block-list">
<li>Raindrop.io — All-in-one bookmark manager<br><a href="https://raindrop.io/">https://raindrop.io/</a></li>
</ul>
<h3 class="wp-block-heading">self-hosted (自託管)</h3>
<ul class="wp-block-list">
<li>Karakeep <br><a href="https://karakeep.app/">https://karakeep.app/</a></li>
</ul>
<ul class="wp-block-list">
<li>wallabag: a self hostable application for saving web pages<br><a href="https://wallabag.org/">https://wallabag.org/</a></li>
</ul>
<ul class="wp-block-list">
<li>go-shiori/shiori: Simple bookmark manager built with Go<br><a href="https://github.com/go-shiori/shiori">https://github.com/go-shiori/shiori</a></li>
</ul>
<ul class="wp-block-list">
<li>Readeck<br><a href="https://readeck.org/en/">https://readeck.org/en/</a></li>
</ul>
</blockquote>
<p>我會選擇 <a href="https://karakeep.app/">Karakeep</a>,最主要的原因有兩個:</p>
<ol class="wp-block-list">
<li>除了 Instapaper 外,現在其他的服務我都沒有把握不會在接下來的十年內消失。</li>
<li>我希望用 AI 來協助處理,而 AI 產生 tag、全文搜尋、支援 <a href="https://www.ithome.com.tw/news/169035">MCP</a> 等,聽起來很吸引人。</li>
</ol>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading" id="-35f21c8b-75be-4cfe-ba60-22f4e56c2e8a">安裝</h2>
<p>現在的軟體安裝,有了 Docker 之後真的很方便。<br>下載 docker-compose.yml 後,把 .env 設好,nginx 的reverse proxy 設一設,避開會衝突的port。機器很順利就run起來了。</p>
<p>可以設定的東西不多,先初步玩一下發現沒什麼大問題,就準備把資料從 pocket 搬過來了。</p>
<p>Pocket 有支援 export 功能,匯出的 .csv Karakeep 也可以匯入。</p>
<p>嗯…從2014 到現在…13000筆書籤。不知道我的小機器吃不吃得下。</p>
<p>跑了兩天,終於重新 index、抓內容和離線頁面、請 AI tag 完成。還好機器撐住了,沒掛掉。</p>
<p>一些統計數字如下:</p>
<ul class="wp-block-list">
<li>13000 筆書籤 ,其中 2285筆 (占17.45%) 現在已經消失,無法再連得到。部份是Google feedproxy 的連結 (又是該死的 google)</li>
<li>下載下來的網頁文字、banner 圖檔、和頁面存檔 (snapshot) 占了 4.79GB 的資源。</li>
<li>Pocket 資料裡的歸檔狀態 (Archived) 沒有過來,導致我必須想辦法把 12900 筆資料設為歸檔。在寫程式用API處理、使用 CLI 處理、以及手動歸檔 (有批次編輯)這三種選項中,我選擇了… 第三種 人工。</li>
<li>AI 產生 tag 消耗了 8鎂 的 OpenAI credit。</li>
</ul>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Update 2025/6/8:</strong><br>v0.25.0 已經支援把 Pocket 的歸檔狀態 (Archived) 一併帶入。但我沒有試過。只是根據 release note 所言。 </p>
</blockquote>
<p>目前手動的書籤管理 (新增/編輯)已經轉到 Karakeep 上作業了。加書籤的速度跟 Pocket 一樣快,但是AI 處理跟 index 需要時間,所以出現在首頁的時間會需要 30秒左右。</p>
<p>再觀察看看,真的受不了,再看看如何改善。</p>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading" id="-be20d2d9-f8d4-4df6-aa55-8434d31c7cfb">還需改善的地方</h2>
<p></p>
<h3 class="wp-block-heading" id="-7a37529c-7775-487c-a3bd-ca143fee6701">書籤進系統後,處理的速度不夠快</h3>
<p>因為要讓 AI 處理過書籤,抓取內容,再讓內建的 Meilisearch index,感覺出現在首頁的速度還是有點慢 (30s 左右),目前還在勉強接受的範圍。</p>
<p>因為在我 GTD 的流程裡面,新增書籤是 Capture 資訊進 InBox,跟後續的 Clarify/Organize 是在不同階段處理的事。所以我有多的時間讓系統去處理。</p>
<p></p>
<h3 class="wp-block-heading" id="-65cbe4c6-45b8-4814-8db8-96b882dc8ebf">半自動化/自動化流程還需要重新打造。</h3>
<p>主要是因為 IFTTT 不支援 Kerakeep。之前的自動發佈流程就失效了。<br>但正好拿來作為我練習 n8n 的作業之一。</p>
<p></p>
<h3 class="wp-block-heading" id="-0a83b561-5a98-4876-8367-81accf929ff4">沒有夠強大的瀏覽器擴充工具</h3>
<p>自從發現 <a href="https://inmypocketaddon.com/">In-My-Pocket</a> 這個 firefox 的擴充之後,我就把Pocket官方的擴充工具給丟了。甚至連官方的首頁都很少上去。</p>
<p>現在 Karakeep 的的瀏覽器工具加書籤很順,但也才僅止而已。要閱讀等還是要去首頁。閱讀/打星/歸檔還是沒有那麼流暢。</p>
<p>我看 In-My-Pocket 的作者也在<a href="https://github.com/pabuisson/in-my-pocket/issues/4">煩惱未來的路怎麼走</a>。我想,就讓子彈飛一會兒吧。</p>
<p></p>
<h3 class="wp-block-heading" id="-884eb4e0-aae8-413b-997b-10dde8ea5084">資源占的硬碟空間有點多</h3>
<p>13000則書籤,即使包括文字,應該不會占太多空間。但實務上占了4.8 GB的空間。初步分析了一下,主要是他抓下來的網頁 banner 並不會處理,就直接存進去。對某些網站一張4K尺寸 PNG 的banner,占了11MB 也照存不誤。累積下來當然很可觀。</p>
<p>看了一下 Karakeep 的資料架構,之後應該可以把這些圖檔取出來處理成尺寸較小、壓縮率較高 (例如 Jpegli 或是WebP) 的檔案再存回去。這就等有空再處理,反正目前還有空間。</p>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading" id="-e2719c0e-4008-44b4-aac3-765f6cbf9b50">未來潛力</h2>
<p></p>
<p>目前除了手動切換到 Karakeep 使用一陣子,看看有沒有什麼致命問題外。<br>有空的時間會先花時間打造輪子,回到之前習慣的工作流程模式。順便升級一下成自己之前想要,但受限於 Pocket 跟 IFTTT 而無法實現的工作流。</p>
<p>之後如果不分心的話,會想把這13000筆資料,請AI幫忙,整合進我在思考時的架構之中。畢竟這些文章我當年都有讀過,只是忘了。 XD 怎麼做還不清楚,但可能用 MCP 去 refer 吧。</p>
<p></p>
<p>還是希望不要再跳船了,都一把老骨頭了…</p>
<p></p>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/06/from-pocket-to-karakeep/feed/</wfw:commentRss>
<slash:comments>2</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/截圖-2025-05-31-下午2.46.02-scaled.png" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8238</post-id> </item>
<item>
<title>白沙屯媽祖進香 – 其他心得</title>
<link>https://blog.serv.idv.tw/2025/05/pilgrimage-other-thoughts/</link>
<comments>https://blog.serv.idv.tw/2025/05/pilgrimage-other-thoughts/#respond</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Fri, 23 May 2025 01:47:00 +0000</pubDate>
<category><![CDATA[白沙屯媽祖徒步進香 (2025)]]></category>
<category><![CDATA[白沙屯媽祖]]></category>
<category><![CDATA[裝備]]></category>
<category><![CDATA[進香]]></category>
<category><![CDATA[遊覽車]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8205</guid>
<description><![CDATA[這應該是我參加 2025 白沙屯媽祖徒步進香的最後一篇。 我把一些比較零碎的想法放在這篇,可能是因為它們的篇幅 ... <a title="白沙屯媽祖進香 – 其他心得" class="read-more" href="https://blog.serv.idv.tw/2025/05/pilgrimage-other-thoughts/" aria-label="Read more about 白沙屯媽祖進香 – 其他心得">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>這應該是我參加 2025 白沙屯媽祖徒步進香的最後一篇。</p>
<p>我把一些比較零碎的想法放在這篇,可能是因為它們的篇幅或完整性沒有值得單開一篇來寫,但這些仍然值得被紀錄下來。</p>
<p>包括有:</p>
<ul class="wp-block-list">
<li><strong>裝備回顧</strong>:在進香結束後,回頭來看當初準備的行李和裝備中,哪些符合預期,哪些需要調整。</li>
<li><strong>遊覽車值不值得</strong>:在個人實際體驗遊覽車之後,和我當初預期不同的地方。如果你是像我一樣類型的香燈腳,請不要選擇坐遊覽車。</li>
<li><strong>我個人喜歡走的路線</strong>。</li>
<li><strong>緣未俱足</strong>的部份:還有來年,隨緣。</li>
</ul>
<span id="more-8205"></span>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading" id="裝備回顧">裝備回顧</h2>
<p>在 <a href="https://blog.serv.idv.tw/2025/04/pilgrimage-preparation/">準備</a> 篇,我有提到我準備裝備的方式。實際走一趟回來,大部份的準備是符合預期的:</p>
<ul class="wp-block-list">
<li><strong>分類裝進小防水袋</strong>:在下雨天真的安心。唯一的小缺點,就是當好幾包都在行李裡的時候,要馬上找出某一包是有困難的。需要摸一下或是拿出來看一下,沒抽到的話再放回去。</li>
<li><strong>備用拖鞋(母子鱷魚)</strong>碰到下雨天後真的很好用,晚上在住宿點移動,盥洗或是散步去買個東西也很方便。我這次因為穿的是五指襪,好像也可以穿五指襪後再穿母子鱷魚步行 (但我沒試)。對了,要準備一個鞋袋或是塑膠袋,裝完溼掉的步鞋後,用D型扣掛在背包後。</li>
<li><strong>雨備包</strong>以這次碰到的雨勢來說,剛剛好。小雨就上背包套,大雨再把輕便雨衣穿上。輕便雨衣還可以拿來鋪在地上當座墊或是床墊。</li>
<li>食物的部份剛剛好。平常雖然用不到 (都有福食和點心攤),但在應急 (過夜) 時能量棒就給了我許多幫助。</li>
<li><strong>醫療包</strong>理論上備而不用,我也真的沒用上。全程唯一用上的那把瑞士刀,某天拿來切開包裝 Orz。<br>不過腿受傷時,我倒是有想過要不要吞個止痛藥然後繼續走。但一來我很少這麼做,二來我沒把握會不會傷勢加重,影響回程,所以就沒試了。</li>
</ul>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/04/IMG_6201s.jpg?resize=1024%2C768&ssl=1" alt="" class="wp-image-8076" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/04/IMG_6201s.jpg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/04/IMG_6201s.jpg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/04/IMG_6201s.jpg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/04/IMG_6201s.jpg?w=1920&ssl=1 1920w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<p>在行程中調整的有:</p>
<ul class="wp-block-list">
<li>外套:照片中那件防風外套,在急行軍之後因為沒有晚上穿著的必要,就移至過夜行李,改用一件薄外套。而急行軍其實以今年的天候而言,也用不到那件防風外套/薄外套。但如果那件防風外套有留在隨身背包,我在大安的那一夜應該就不會半夜被冷醒/疑似失溫。</li>
<li>防曬包裡<strong>大遮陽帽</strong>在去程急行軍後就被我挪到過夜包。當初考量是較大的尺吋可以保護脖子和肩膀不被曬傷。實作上發現官方鴨舌帽加上毛巾披在後腦就有一樣的效果。而言毛巾還可以沾冰水打溼達到更好的消暑效果。移到過夜包後,我的隨身背包又多了一些空間。</li>
<li><strong>夜行包</strong>裡的東西用上的真的不多,只有夜間警示燈有用到 (掛在背包後),其他包括頭燈都沒用上。主要原因有三:
<ol class="wp-block-list">
<li>去程急行軍第一夜 (白沙屯到大安)人很多,基本上沒有用上頭燈的機會。</li>
<li>去程急行軍第二夜 (麥寮王功鹿港段) 我DNF沒下去走, 後來看其他人的影片,這一段隊伍拉太長,可能會有獨走的情形,加上路燈照明較不足,應該會用得上頭燈。</li>
<li>回程大多是白天徒步,也沒有用上燈的地方。<br>雖然這次沒用上頭燈,但為了安全起見,我仍然建議要把頭燈放進隨身背包中。</li>
</ol>
</li>
<li><strong>充電包</strong>這次還好有買備用行動電源,但我把其中一顆放在過夜行李。所以實際帶在隨身背包的是兩顆。我正常使用的話,一天大概會用掉一顆10000的行動電源。第二顆除了安心,也讓我在外頭過夜時,不會太擔心第二天的行程。但最麻煩的是 — 充電。在行程中,不管是急行軍還是平日步行,是找不到什麼機會停下來充電的。以現在的充電速度,每次充電大概要停下來40~60分鐘,其實不是很划算。我後來的作法是:用完的電池放到過夜行李,然後過夜時在住宿點的插頭搭快充頭充– 這樣一定可充飽。如果住宿點沒有機會,也可以靠遊覽車上的USB 插頭充 (雖然充電速度非常慢),但我早上就下車、傍晚才上車,通常都有足夠的時間充飽。 如果未來要自己露宿,就要好好考慮充電的問題。我在路上有看到帶著太陽能充電板 (我原定要帶的那種) 徒步的人。而這幾天的天氣都比預期的炎熱,我想充電效率應該蠻好的,只是沒機會跟他聊實際使用狀況。可能要找機會自己測試一下。 另外一個意料之外的電力消耗,是手錶。 Garmin 955 開GPS mode 撐不過10hr,不管急行軍或是一般徒步都不夠用。我後來是開 UltraTrac,然後關掉跟手機的連線,有40小時以上的績航力,不過路線精準度就差強人意了。 總之,手錶充電線還是要帶,不然會GG。我也考慮過邊充手錶邊走的選項 (用行充),但這樣應該會有一段的心率資料是缺少的,而且不知道會不會有其他數據受到影響。</li>
<li>可以準備一個<strong>小提袋 (小的就好) 和 幾個可提的塑膠袋</strong>。小提袋可以手提或是掛在背包前面 (我在背帶上用D型扣掛著,重心在胸到腰間),有結緣、點心、零食、或是福食可以先放入袋中,不用停下來放背包或一直手拿著。小塑膠袋放在小提袋中,吃完的包裝、空瓶等先放到袋裡,等到垃圾回收點可以一次清空。 這個提袋的重量也可以提醒你,是不是已經拿太多了?夠了就好。隨緣。</li>
</ul>
<p>整體重量 3.7Kg (不含行程中動態出現在提袋裡的福食和零食),再加上腰帶和胸袋的協助,我覺得背起來不累。如果要再加上露宿的裝備,可能會再加個 __ Kg …吧。這個明年再思考規劃好了。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading" id="遊覽車值不值得報名-乘坐">遊覽車值不值得報名/乘坐?</h2>
<p>之前我有<a href="https://blog.serv.idv.tw/2025/03/nomad-50-pilgrimage-registration/">提到</a>,自己會報名步行遊覽車的原因:</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>進香除了全程徒步外,還有所謂的(官方)步行遊覽車。這當然不是指全程坐在遊覽車上,而是在車上有個位子可以放行李,每天晚上還可以幫你訂好住宿 (通常是一定距離廟宇的香客大樓),載你去投宿休息,隔天早上再載你回來繼續徒步。對我而言,這樣子省去了餐風露宿的準備以及行李的負重。對於第一次參加的我,這是一個相對輕鬆的選擇,尤其在面對未知的進香旅程時,能夠有這份保障着實讓我安心不少。</p>
</blockquote>
<p>而在報名當天排隊時,前方有一位大姐也跟我分析了遊覽車方案的優缺點:</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>她說遊覽車比較辛苦的,是在一天的步行後,還要再多走一段路去遊覽車停車的地方。通常因為空間的關係,遊覽車會停在外環道附近,這代表要多走30~60分鐘的路程。「已經走了十幾個小時,還要再多走一個小時,真的很累」。另外一個缺點,就是住宿的點車程約1小時,所以休息更晚,出發更早,壓縮到時間。但是好處是不用擔心睡覺和洗澡的地方。</p>
</blockquote>
<p>我自己今年搭步行遊覽車的經驗,對於「多走一段路去遊覽車停車的地方」已經有心裡準備,所以並沒有什麼不適應的地方。但我碰到的遊覽車口頭上雖然說「會等到媽祖駐駕後再載我們去住宿點」,但實際上卻會找一些理由提早收人,像是「山路遊覽車開上去會塞太久。」「61平面大塞車,請大家開始往回走」「媽祖繼續走,但是住宿點廟門8點就關,所以要提早走。」…等等。所以對我而言,這次最大的影響,就是因為遊覽車提早收人,而少走了好幾段。</p>
<p>有機會碰到其他遊覽車的香燈腳,一聊之下,發現其實各遊覽車的作法相差很多,評價也不一。有的就會開上松柏嶺、開到大安去 (當然到住宿點的時間就晚了);有的則是挑近一點但是貴不少的住宿點,像我有碰到跟我抱怨每晚住宿費要1000元的 (但我反而覺得這比駐駕在台中,住宿卻在雲林來得好吧)。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6579.jpeg?resize=1024%2C768&ssl=1" alt="" class="wp-image-8177" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6579-scaled.jpeg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6579-scaled.jpeg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6579-scaled.jpeg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6579-scaled.jpeg?resize=2048%2C1536&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6579-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure></div>
<p>簡單說,如果你像我一樣有一點認真,想要當個「認真的人」,在可能的情況下儘量步行。那麼步行遊覽車…不適合你。</p>
<p>如果你想要的行程是坐在遊覽車上,找時間下去跟媽祖走一段,或是一邊吹著遊覽車的冷氣,一邊看媽祖的鑾轎經過,那麼步行遊覽車會是一個不錯的選擇 — 當然,還要看看你碰到的車掌和司機怎麼掌握停車點和移動的方式。</p>
<p>以我而言,明年應該不會再報名遊覽車了。這意味著我要克服過夜的問題,以及相對應的行李負重。</p>
<p>怎麼做? 明年再來想好了…</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading" id="我個人喜歡走的路線">我個人喜歡走的路線</h2>
<p>這是我第一次走,回想起走過的路線,包括省道、快速道路、縣道、沿途經過大城鎮、小城鎮等等。其實很難說自己最愛或是最不喜歡哪一段。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="1024" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-1024x1024.jpeg?resize=1024%2C1024&ssl=1" alt="" class="wp-image-8127" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=1024%2C1024&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=400%2C400&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=150%2C150&ssl=1 150w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=1536%2C1536&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=2048%2C2048&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=120%2C120&ssl=1 120w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure></div>
<p>官方說法當然是:「媽祖走哪裡,我就走哪裡。」</p>
<p>如果以「步行體驗」而言,以下是我的粗淺印象:</p>
<ul class="wp-block-list">
<li>走在城鎮和城鎮之間的省道 (台1線),雖然補給很多,但相對而言是比較枯燥的。像從溪湖走到彰化市的這一段,就是沿著省道走呀走呀走…</li>
<li>走進小鄉鎮或是縣道等,雖然人會塞到滿出來,但保持距離的話,路邊的風景和經過的村里、居民,都讓人比較有「進香」的感覺。像這次走進林內、繞到六房媽那邊,都有這種感覺。</li>
<li>走在大城鎮裡,有參加嘉年華會的感覺,一直吃一直吃一直喝一直喝。像通霄和北港都給我這種強烈的感覺。</li>
<li>走在 61下平面道路,或是沿著堤防邊走到和美,真的有「行軍」的感覺。一個車道無限延伸,放眼望去都是橘帽。路邊隨時有人或坐或躺,有的人會願意停下腳步,看看日出的美好風景;有的人願意鋪起墊子,好好睡個一兩個小時。雖說是行軍,雖說目標不變,但每個人的作法還是不一樣的。是不是那麼「認真」,在這個時候,真的需要計較嗎?</li>
</ul>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading" id="緣未俱足的部份">緣未俱足的部份</h2>
<p>以下是這次「緣未俱足」的部份,未來有機會,希望能夠結緣:</p>
<ul class="wp-block-list">
<li>去程急行軍 DNF</li>
<li>回程第一天因為腳傷休養沒走,其他天有的受遊覽車影響,提早下馬,沒跟到駐駕地。</li>
<li>找了孩子要一起體驗半日的步行 (通霄車站段),但因為大雨以及人實在太多,孩子並沒有實際體驗跟媽祖走一段路。他們也沒有太多點心攤和福食結緣的體驗。</li>
<li>自己事前並沒有準備什麼結緣品,後來都是把其他人跟我結緣的種種紀念品,再次結緣出去給需要的人。</li>
</ul>
<p>雖說緣未俱足,但這一次學到的,得到的,媽祖額外給我的,已經很多很多,讓我受用無窮了。<strong>也希望這一系列的文章,可以跟有緣的人結緣</strong>,讓有興趣的人,更了解這個活動,更了解如何準備,以及更了解2025年的這次北港徒步進香,帶給我這個菜鳥香燈腳,什麼樣子的美好體驗。</p>
<figure class="wp-block-image alignwide size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7229.jpeg?resize=1024%2C768&ssl=1" alt="" class="wp-image-8184" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7229-scaled.jpeg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7229-scaled.jpeg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7229-scaled.jpeg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7229-scaled.jpeg?resize=2048%2C1536&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7229-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">系列文章閱讀:</h2>
<p>以下是關於2025我參加白沙屯媽祖進香的系列文章,供您快速一口氣閱讀:</p>
<ol class="wp-block-list">
<li><a href="https://blog.serv.idv.tw/2025/03/nomad-50-pilgrimage-registration/">遊民週記 50: 白沙屯媽祖進香 – 報名</a></li>
<li><a href="https://blog.serv.idv.tw/2025/04/pilgrimage-preparation/">白沙屯媽祖進香 – 準備</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-day1-day2-dnf/">白沙屯媽祖進香 – 去程急行軍</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-step-into-nantou/">白沙屯媽祖進香 – 見證歷史,走入名間</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/">白沙屯媽祖進香 – 台中大安的奇幻旅程</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-types-of-followers/">白沙屯媽祖進香 – 香燈腳眾生百態</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-other-thoughts/">白沙屯媽祖進香 – 其他心得</a></li>
</ol>
<p>如果你對這個主題有興趣,可以閱讀<a href="https://blog.serv.idv.tw/category/tour/baishatun-mazu/" target="_blank" rel="noreferrer noopener">同分類的文章</a>,或是點選相關的關鍵字,作更多的閱讀。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p></p>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/05/pilgrimage-other-thoughts/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7230-scaled.jpeg" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8205</post-id> </item>
<item>
<title>白沙屯媽祖進香 – 香燈腳眾生百態</title>
<link>https://blog.serv.idv.tw/2025/05/pilgrimage-types-of-followers/</link>
<comments>https://blog.serv.idv.tw/2025/05/pilgrimage-types-of-followers/#respond</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Wed, 21 May 2025 01:15:00 +0000</pubDate>
<category><![CDATA[白沙屯媽祖徒步進香 (2025)]]></category>
<category><![CDATA[白沙屯媽祖]]></category>
<category><![CDATA[社會觀察]]></category>
<category><![CDATA[進香]]></category>
<category><![CDATA[香燈腳]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8172</guid>
<description><![CDATA[香燈腳也是眾生,是眾生就有百態。有認真魔人、有堅定信仰者、當然也有一日遊、大型嘉年華、或是純粹跟風打卡的。 每 ... <a title="白沙屯媽祖進香 – 香燈腳眾生百態" class="read-more" href="https://blog.serv.idv.tw/2025/05/pilgrimage-types-of-followers/" aria-label="Read more about 白沙屯媽祖進香 – 香燈腳眾生百態">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>香燈腳也是眾生,是眾生就有百態。有認真魔人、有堅定信仰者、當然也有一日遊、大型嘉年華、或是純粹跟風打卡的。</p>
<p>每個人都在用自己的方式追隨媽祖,每個人都有自己的原則。</p>
<p>以下是我所觀察到的,一些切片和採樣。</p>
<span id="more-8172"></span>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">從大陸五一長假回來的香燈腳</h2>
<p>在往白沙屯的列車上,碰到了一行七人,嘻嘻哈哈的香燈腳們。<br>兩三個老鳥帶著幾個菜鳥一起走,聽起來像是曾經的同事或同學,現在在不同地點工作著。<br>聊天的話題有不少是某些共同的朋友,現在的工作和近況。</p>
<p>因為是老鳥帶菜鳥 (第一年走,跟我一樣),在火車上,他們正在進行「行前教育」,包括白沙屯媽祖的介紹、接下來的行程,急行軍晚上的衣著,關於背包重量、哪些要帶哪些不要帶 (即使已經在火車上了)。</p>
<p>為什麼要來徒步進香呢?應該是有經驗的老鳥揪團的。一起在五一這個假期體驗這個文化盛會。</p>
<p>現在回想,因為沒看到過夜用的行李,衣著也以休閒為主,我猜測他們應該沒有要走全程,可能走的是當晚的出發跟接下來的急行軍。</p>
<p>後來在路上,某個前同事也有聯絡上我,坐高鐵下來跟我一起走了半天。我也是從白沙屯媽祖進香開始介紹,想一想,可能新手教學都是長這樣吧。</p>
<h2 class="wp-block-heading">遊覽車隔壁座位的方大哥</h2>
<p>第一天check in 遊覽車,在車內等出發休息的時候,坐我隔壁的一位方大哥。他近幾年從上一份工作退下,回家照顧年邁的母親,並接下家裡的生意。因為他不是第一次走,所以這時候是他給我「新手教學」,說了一些要注意的事項。尤其是要如何避免受傷。包括走一走腳底出現熱熱的感覺就一定要休息;出現剌剌時就接近來不及,會有大受傷;當痛起來的時候,代表傷勢已經造成,已經來不及了。</p>
<p>另外他也告訴我,瓶裝水要備好兩瓶,小的 (300cc)左右。大瓶的會太重,其中一瓶作為某些路段補給不及時,喝水之用。我後來備水的策略就此演變而來:常飲用瓶 300或600cc (白天中午)、另一瓶備用 (200/300cc)。</p>
<p>為什麼要來徒步進香呢? 他從更早時照顧父親說起,從照顧父親,再到現在照顧母親。來徒步進香,為家人的健康祈福,是他的初衷之一。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="768" height="1024" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6379.jpeg?resize=768%2C1024&ssl=1" alt="" class="wp-image-8176" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6379-scaled.jpeg?resize=768%2C1024&ssl=1 768w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6379-scaled.jpeg?resize=300%2C400&ssl=1 300w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6379-scaled.jpeg?resize=1152%2C1536&ssl=1 1152w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6379-scaled.jpeg?resize=1536%2C2048&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6379-scaled.jpeg?w=1920&ssl=1 1920w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure></div>
<p>聊著聊著,他聊到他的孩子。聽起來,他的孩子也大了 (高中以上),這幾天他不在家,也不用擔心孩子們的生活作息。反倒是某個近親的孩子,還需要他處理。一聊一下,才發現他的理念是家族要互相照顧/照應彼此的下一代,有點像是過去的「抱團相互扶持」。現在少子化,等孩子長大後,碰到什麼狀況,家族們便可以互相支援,彼此照應。至於孩子自己,想做什麼、想選什麼,就讓下一代自己去發揮。他只要準備好家族互助安全網就好了。</p>
<p>這種教育理念對而我而蠻新奇的。聽起來方大哥應該也是很有想法的人。我很期待接下來的幾天,身為遊覽車的鄰居,我有機會聽他說得更多。</p>
<p>沒想到這是我第一次,也是最後一次跟他碰面。後來聽車掌說,他去程中途就離開了。</p>
<p>一期一會,這是進香路程中,最常見的人際樣態。</p>
<p></p>
<h2 class="wp-block-heading">幾天後出現在我隔壁座位的泰山大哥</h2>
<p>回程忘了是第幾天,遊覽車上來一個新的團客。我們就叫他(張)泰山大哥好了。</p>
<p>他的個性豪爽,樂於分享,對於徒步進香而言,有一定的認真程度。</p>
<p>為什麼要來徒步進香呢?他說好幾年前他就有參與大甲媽祖繞境了,這幾年又參加白沙屯媽祖進香。</p>
<p>我心裡飄過的第一個念頭是:「 哇,那你前陣子才去大甲媽祖繞境,現在又來白沙屯,不會累嗎?」<br>第二個念頭是:「那你的工作是什麼,怎麼能夠請那麼多天假啊?」</p>
<p>不過我們完全沒有聊起這個話題。</p>
<p>因為是稍微認真型的香燈腳,對於全程徒步、上馬下馬等儀式,他有一定的要求。跟我一樣,他希望每天從媽祖起駕走到媽祖停駕,所以針對遊覽車提早收人這件事,他其實很不爽 (跟我一樣很不爽)。也不只一次在LINE 群組裡表達不滿。我記得後面幾天。其中一天是他決定跟著媽祖走上松柏嶺受天宮,然後隔天再上車拿行李;另一天遊覽車一樣提早收人,他沒有跟媽祖走到溪湖糖廠 (因為背包裡的換洗衣服已經在受天宮那一夜用掉了),只好跟我坐在遊覽車上被載到住宿點,然後一邊看著直播一邊抱怨著;再隔天,駐駕彰化市的時候,沒有看到他上車;再下一天,就是我自己魔幻的一天,自己露宿在外。然後…我們就沒碰過面了。</p>
<p>「那你怎麼會選遊覽車,而不是跟一般香燈腳一樣睡外面?」有一次我問他。</p>
<p>『啊就我行李太多,所以才選遊覽車。』他帶點靦腆地笑了笑,這樣告訴我。他的過夜行李是一個大大的旅行箱,我不知道裡頭裝了多少東西。但他的隨身行李就跟路上看到的認真型香燈腳沒兩樣,只是少了睡墊跟帳篷而已。</p>
<p>他的背包裡甚至還有一套換洗衣服,讓他可以在外面一宿。</p>
<p>他也教了我一些有用的走法,包括走在轎前 3-5分鐘處。人較少較好有自己的節奏。不要走在轎邊或轎後方。如果走錯,就轉向追。然後經過轎邊時跟警察或廟方交通組的人說:我是要往前通過的,他們就會讓你儘快通過。</p>
<p>如果是急行軍,他就會提早1個小時出發,然後在61通霄連絡道那邊休息;可以等鑾駕決定走哪條路,也可以自己走橋下 (反正高架橋上的走一走,也會下閘道,他說)。急行軍時走在前方,才有休息的時間。</p>
<p>我們也聊到最後一天,也就是媽祖停駕慈后宮後,隔日到拱天宮的這一段,前一夜要如何住宿?他覺得最後一天人太多,其實沒有很好走,他自己並不強求要走這一段。但如果要走,前一夜要住哪裡?他有一個小秘訣跟我分享:徒步途中,可以找個火車站先坐到白沙屯站,然後到拱天宮餐廳樓上占個鋪位,再坐火車回來,然後花點時間跟上大軍。</p>
<p>跟方大哥一樣,我也是來不及跟他道再會,我們兩人就消失在對方的生命裡了。</p>
<p></p>
<h2 class="wp-block-heading">坐遊覽車,每天早上起駕前下車,走一段路看狀況就上車休息</h2>
<p>遊覽車上最常見的樣態,就是60~70歲,退休之後,來參加進香的大哥大姐們。<br>因為體力的關係,他們不會走完全程,走路的時間大部份是在每天早上起駕前,然後走一段路,看個人體力狀況上車休息。通常下午因為天氣比較熱,他們就不會下車去走。晚上則是等遊覽車收人之後,跟著大隊人馬一起去住宿點休息。(而我,通常就是被他們等著收人的固定那幾位)</p>
<p>為什麼要來徒步進香呢? 每個人的答案應該有所小小不同。但我想「進香」這兩個字,在傳統坐遊覽車去進香的行程裡面,這樣坐遊覽車、下去走走、晚上去住宿點,已經是最接近的。</p>
<p>進香對他們而言,比較像是某個旅遊行程。</p>
<p>某個剛退休沒幾年的大哥,說「這禮拜來進香、下禮拜又揪團去泰國」<br>『哇,你怎麼行程這麼滿,這麼忙?』<br>「不然退休後要幹嘛?當然是到處走走到處看看囉!」</p>
<p>而另外一個特色,就是他們不一定全程,大部份去程急行軍是坐遊覽車、有的是回程才加入、有的人幾天後就消失,然後把名額讓給朋友/姐妹/兒女。然後一兩天後,又沒看到朋友/姐妹跟兒女了。</p>
<p>現在想想,其實有點像服務時間較長的「累了請上車」一樣。<br>這可能才是徒步遊覽車服務客群的真正樣態,跟我一開始以為「媽祖起駕開始走,駐駕後上車去住宿點」是有很大的落差的。</p>
<h2 class="wp-block-heading">坐遊覽車但沒什麼下去走,坐在車上看著豬哥亮歌廳秀、一邊喝酒的人</h2>
<p>這是少數。但我們這台車上就有一位。70幾的年紀,是很典型的 E 人大哥,聲音洪亮,坐在遊覽車的最前頭,講話的聲音在最後面都聽得很清楚。</p>
<p>為什麼要來徒步進香呢?不知道,也許是兒女叫他出來走一走。但我不太敢開話題,怕一開下去就沒完沒了。</p>
<p>記得有一次,在北港附近,應該是他的孩子打了電話來,要他帶火柴回去。</p>
<p>為什麼要帶火柴呢? 電話那頭跟他講了「馨(薪)火相傳」的典故,可以讓生意興隆。結果他對電話那一頭喊 (好啦,是說,但因為太洪亮,聽起來像是喊):「這麼旺,我不就得做到死?我才不要~」</p>
<p>掛上電話,他的手機繼續撥著豬哥亮的歌廳秀,搭配大量的罐頭笑聲。</p>
<p>有時候遊覽車停得比較久,他會下車拉張椅子,吹著風,配著油飯和啤酒,真的有他自己生活的一套。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p>聊完在遊覽車上看到的,接下來聊聊在步行途中遇到的。<br>這些往往是一期一會,某次休息,某次用餐時遇到了,聊個幾句,共處15分鐘,然後,又各自出發,消失在茫茫人海裡。</p>
<p>因為沒什麼機會深聊,往往我觀察到的,就只是片面那瞬間的角度切片。但還是分享一下:</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="1024" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6697-scaled-e1747745324196-1024x1024.jpeg?resize=1024%2C1024&ssl=1" alt="" class="wp-image-8180" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6697-scaled-e1747745324196.jpeg?resize=1024%2C1024&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6697-scaled-e1747745324196.jpeg?resize=400%2C400&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6697-scaled-e1747745324196.jpeg?resize=150%2C150&ssl=1 150w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6697-scaled-e1747745324196.jpeg?resize=1536%2C1536&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6697-scaled-e1747745324196.jpeg?resize=120%2C120&ssl=1 120w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6697-scaled-e1747745324196.jpeg?w=1920&ssl=1 1920w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure></div>
<h2 class="wp-block-heading">從附近鄉鎮騎車過來體驗的人</h2>
<p>某天吃晚餐時,碰到兩個年輕的青年,看起來剛成年。我們有幸併桌坐在同一桌,吃著吃著,就聊起來了。</p>
<p>他們住在附近的鄉鎮,聽說媽祖來到這個城鎮,所以騎車過來想要體驗體驗進香。</p>
<p>「你可以跟我說,這頂帽子要去哪裡買嗎?」其中一個弟弟問。<br>『那個要報名的啦!』另一個弟弟跟他說。</p>
<p>另一天休息時,一樣是兩個年輕的青年,討論著改車的問題。身上沒有任何步行的裝備。</p>
<p>「你們兩個少年A 是要一起走嗎?」一個也在休息的大哥問。<br>『沒啦,我們是騎車過來看熱鬧,不過現在車子騎不出去了…」</p>
<h2 class="wp-block-heading">一期一會.結緣分享.緣份俱足</h2>
<p>在路上會碰到很多自己準備一些小禮物,跟別人「結緣」的香燈腳。<br>但有的人看到有小禮物/紀念品就擁上去索取,反而變成了「劫緣」。</p>
<p>我比較有印象的,是在梧棲某次休息時,某位大姐跟我分享她的小秘訣:休息時,把鞋襪脫了,然後把薄荷油塗在小腿和腳底,清清涼涼地可以抵抗熱熱的腳底。離開前,她跟我結緣了一小瓶薄荷油。</p>
<p>另外一個場景也類似,在土庫六房媽那邊停駕休息時,一樣是某位大姐跟我分享了類似的秘訣 — 用的是她自製的左手香。我初次見識到左手香,還向她請教了不少。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1162.5" height="1163" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?resize=1162.5%2C1163&ssl=1" alt="" class="wp-image-8188" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?w=2560&ssl=1 2560w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?resize=400%2C400&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?resize=1024%2C1024&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?resize=150%2C150&ssl=1 150w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?resize=1536%2C1536&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?resize=2048%2C2048&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?resize=120%2C120&ssl=1 120w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6665-edited-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1162px) 100vw, 1162px" /></figure></div>
<p>而沒有準備結緣品的小白如我,後來的作法是 — 把緣份再分享出去。</p>
<p>當我走到田中時,碰到了久違的朋友。朋友並沒有參加進香,但是對於這個活動很感興趣。於是我解下背包上陪我數天,也是別人送我的進香紅條 (抱歉,我不知道正確名稱為何),然後送給他。雖然不是全新的,但陪我一起走了幾天,應該更有意義才是。看她開心地和同事分享著,當時我更體驗到結緣不只是收,還有分享。</p>
<p>後來,如果碰上機會,例如可愛的孩子在人群中跟著爸媽行走,或是在點心攤幫忙發些茶水,我會把我背包上、袋子裡適合他們的小禮物與他們結緣。如果當時身上沒有適合的東西呢?那就是緣份未俱足啦,我這樣告訴自己。</p>
<h2 class="wp-block-heading">手牽著手的人</h2>
<p>單人走法比較輕鬆。兩人同行若要併行,體力消耗較大(人太多)。且節奏和腿力不同,人多時候還要回頭/探頭留意同行朋友的位置。碰到需不需要休息,需不需要補給的場合,都會影響節奏。</p>
<p>而且單人較能專注個人的體會。</p>
<p>所以我比較喜歡自己一個人走。</p>
<p>但兩人以上,甚至成群結隊也是有好處的。彼此照應、又有話題,說說笑笑,往往疲累就這樣過去了。應該也是一種走法。</p>
<div class="wp-block-jetpack-tiled-gallery aligncenter is-style-rectangular"><div class=""><div class="tiled-gallery__gallery"><div class="tiled-gallery__row"><div class="tiled-gallery__col" style="flex-basis:33.33333%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6350-768x1024.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6350-768x1024.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6350-768x1024.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6350-768x1024.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6350-768x1024.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6350-768x1024.jpeg?strip=info&w=1920&ssl=1 1920w" alt="" data-height="2560" data-id="8173" data-link="https://blog.serv.idv.tw/?attachment_id=8173#main" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6350-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6350-768x1024.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 1 張圖片,共 3 張圖片"/></figure></div><div class="tiled-gallery__col" style="flex-basis:33.33333%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6837-768x1024.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6837-768x1024.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6837-768x1024.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6837-768x1024.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6837-768x1024.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6837-768x1024.jpeg?strip=info&w=1920&ssl=1 1920w" alt="" data-height="2560" data-id="8181" data-link="https://blog.serv.idv.tw/?attachment_id=8181#main" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6837-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6837-768x1024.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 2 張圖片,共 3 張圖片"/></figure></div><div class="tiled-gallery__col" style="flex-basis:33.33333%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6852-768x1024.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6852-768x1024.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6852-768x1024.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6852-768x1024.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6852-768x1024.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6852-768x1024.jpeg?strip=info&w=1920&ssl=1 1920w" alt="" data-height="2560" data-id="8182" data-link="https://blog.serv.idv.tw/?attachment_id=8182#main" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6852-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6852-768x1024.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 3 張圖片,共 3 張圖片"/></figure></div></div></div></div></div>
<p>我佩服 (且羨慕的),是手牽著手,一起走的人。那是一種承諾和力量。</p>
<p></p>
<h2 class="wp-block-heading">發願的人</h2>
<p>談到承諾和力量,當然也有很多「發願」/「還願」的人,跟著媽祖進香。</p>
<p>為了家人/另一半/小孩是常見的,有的有自己的故事,需要深聊後才會知道;有的則是在背包後掛上牌子,走在附近的香燈腳們,一看就可以知道來龍去脈了。祈禱健康的、祈求學業的、祈求平安的、祈望風波平息的。我自己從「參與這項盛事、完成 bucket list的一項」,到後面每天上馬/下馬以除了稟告,也加上了祈願的內容。</p>
<p>看著、感受著、甚至體驗著這種發願 (祈求)和還願 (感謝)過程, 從想要一班「累了請上車」,到天氣太熱來了一陣涼風,到補完沒有走過的橋、越過之前止步的溪,再到補考挑戰無傷完成 50K,再到給我體驗露宿過夜 (內容可見 <a href="https://blog.serv.idv.tw/2025/05/pilgrimage-day1-day2-dnf/">急行軍</a> 和 <a href="https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/">大安急幻旅程</a> 這兩篇),有的是即時的,有的是事後回想時發現的。</p>
<div class="wp-block-jetpack-tiled-gallery is-style-square"><div class="has-rounded-corners-10"><div class="tiled-gallery__gallery"><div class="tiled-gallery__row columns-2"><div class="tiled-gallery__col"><figure class="tiled-gallery__item"><a href="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg?resize=600%2C600&strip=info&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg?resize=900%2C900&strip=info&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg?resize=1200%2C1200&strip=info&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg?resize=1500%2C1500&strip=info&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg?resize=1800%2C1800&strip=info&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg?resize=1920%2C1920&strip=info&ssl=1 1920w" alt="" data-height="2560" data-id="8185" data-link="https://blog.serv.idv.tw/?attachment_id=8185#main" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7282-768x1024.jpeg?ssl=1&resize=1920%2C1920" data-amp-layout="responsive" tabindex="0" role="button" aria-label="以全螢幕開啟第 1 張圖片,共 4 張圖片"/></a></figure></div><div class="tiled-gallery__col"><figure class="tiled-gallery__item"><a href="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg?resize=600%2C600&strip=info&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg?resize=900%2C900&strip=info&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg?resize=1200%2C1200&strip=info&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg?resize=1500%2C1500&strip=info&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg?resize=1800%2C1800&strip=info&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg?resize=1920%2C1920&strip=info&ssl=1 1920w" alt="" data-height="2560" data-id="8183" data-link="https://blog.serv.idv.tw/?attachment_id=8183#main" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7099-768x1024.jpeg?ssl=1&resize=1920%2C1920" data-amp-layout="responsive" tabindex="0" role="button" aria-label="以全螢幕開啟第 2 張圖片,共 4 張圖片"/></a></figure></div></div><div class="tiled-gallery__row columns-2"><div class="tiled-gallery__col"><figure class="tiled-gallery__item"><a href="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg?resize=600%2C600&strip=info&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg?resize=900%2C900&strip=info&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg?resize=1200%2C1200&strip=info&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg?resize=1500%2C1500&strip=info&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg?resize=1800%2C1800&strip=info&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg?resize=1920%2C1920&strip=info&ssl=1 1920w" alt="" data-height="2560" data-id="8178" data-link="https://blog.serv.idv.tw/?attachment_id=8178#main" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6629-768x1024.jpeg?ssl=1&resize=1920%2C1920" data-amp-layout="responsive" tabindex="0" role="button" aria-label="以全螢幕開啟第 3 張圖片,共 4 張圖片"/></a></figure></div><div class="tiled-gallery__col"><figure class="tiled-gallery__item"><a href="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg?resize=600%2C600&strip=info&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg?resize=900%2C900&strip=info&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg?resize=1200%2C1200&strip=info&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg?resize=1500%2C1500&strip=info&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg?resize=1800%2C1800&strip=info&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg?resize=1920%2C1920&strip=info&ssl=1 1920w" alt="" data-height="2560" data-id="8191" data-link="https://blog.serv.idv.tw/?attachment_id=8191#main" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7285-768x1024.jpeg?ssl=1&resize=1920%2C1920" data-amp-layout="responsive" tabindex="0" role="button" aria-label="以全螢幕開啟第 4 張圖片,共 4 張圖片"/></a></figure></div></div></div></div></div>
<p>而在個人的體驗之外,聽著其他的人訴說發生在各自身上的神奇體驗、說著當年自己的發願和現在的還願。即使科學不太容易解釋這些客觀的物理行為,但我現在相信這是經由意識和潛意識的解讀,進而影響了事件的發展以及對事情的看法。也就是說,我們可以把數十萬的進香,看作是一場<strong>超大型的群體心靈治療</strong>。</p>
<p>在這個超大型的群體心靈治療中,每一個香燈腳的身影和故事,都如同一粒粒細沙,匯聚成一股強大的力量。這個力量藉著言語、文字、和行為影響著其他香燈腳,影響著他們對事情的看法,對世間祈求之物、不平之事的看法。而在經歷洗滌過後,也許外部的事實不變,但觀看的角度、詮釋、和應對行為已經不同,後續對心靈、身體影響、和事件發展也就會有不同。當用正面的方式去解讀後,這個善的循環的個案體驗,就會變成下一次上路的「還願」,就會變成下一則在休息時與其他香燈腳分享的神蹟,就會變成下一次超大型群體心靈治療的力量之一。</p>
<p>這是我目前對「發願的力量」的解讀,也是我這次參加進香,最大的意外收穫之一。即使現在回到現實生活之中,我仍提醒自己用進香時心理的角度去解讀事件與反應。這往往給我和之前不一樣的心境與視野,更加從容且隨緣。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">認真的人,以及認真魔人</h2>
<p>當然,看到那些參加進香已經5年、7年、甚至10年的前輩大哥大姐們。自己也會很仔細去看觀察他們的裝備、他們的行為、他們步行的方式、以及他們對其他香燈腳的看法。大部份都是閒聊時聽來的。畢竟在我生活中,還沒有機會認識這樣的人,能夠好好深聊請教。</p>
<p>我叫他們「<strong>認真的人</strong>」。</p>
<p>他們有的走在鑾轎的前後左右,但跟其他擠在媽祖附近的信徒有一個很大的不同:他們通常不會拿起手機一直拍、一直錄。</p>
<p>他們有的走在媽祖的前頭,如果發現鑾轎的路徑和預期的不一樣,就在附近找一條平行的,繼續走下去。也許是一個小時,也許是兩三個小時,也許到當天駐駕,總是會再回到鑾轎面前,稟告下馬。</p>
<p>他們有的走在隊伍的很後頭。有的是因為年紀的關係體力不好,或是身體殘缺因素;有的是拉/推了一台推車,詳細的原因我也不清楚。但他們就是默默地走完一天,一天又一天。就算沒有手機APP,就算走過的鄉鎮點心攤已經撤收,他們還是依靠強大的決心,謝絕一次又一次的「累了請上車」邀約,朝媽祖的方向邁進。</p>
<p>認真的人每個都有自己的故事,有機會的話,你會聽得到。但通常在路上,他們都不是多話的人,都不是說說笑笑的人,所以你能察覺他們的存在,但不一定走進他們的內心。畢竟如果把這段旅程當作是修行,他們就是正在修行途中,心中有所專注之事,沒有要跟你嘻嘻哈哈的。</p>
<p>當然,也有例外。</p>
<p>在松柏嶺受天宮的早晨,我在等起駕前,啃著自己剛拿到的早餐。坐在我旁邊的,是一個皮膚焦黑的年輕人。他主動跟我搭談,問我是不是高雄人。我有些驚訝,竟然有人才兩句話就聽出我的口音? 他是高雄五甲人。走好幾年了。他是我此行碰到第一個,全程徒步完成去程急行軍的。雖然下午兩點左右才到北港,比媽祖晚了2個小時,但自己完成這個挑戰,讓我非常敬佩。他教我一個秘訣:如果真的跛腳的時候就跟媽祖稟告,請媽祖保佑自己才能夠跟著媽祖走完。他說他這樣子做,走著走的就沒那麼痛了。</p>
<p>大部份前輩高手,都有自己的一套。我也在這幾天中,慢慢建立自己的一套走法 (寫在 <a href="https://blog.serv.idv.tw/2025/05/pilgrimage-day1-day2-dnf/">去程急行軍</a> 的最後一part)。相信未來除了繼續改良屬於我自己走法的版本之外,也會持續建立屬於我自己關於背包、過夜、休息等等的版本。</p>
<p>說到版本,某次在休息的時候,聽到附近幾個也是老資格香燈腳的閒聊,分享著他們關於媽祖駐駕後行動的經驗談。話題一開始還算平和,大家各自分享著自己的習慣和經驗,但隨著討論越來越熱烈,語氣也漸漸有些激動。應該是關於駐駕後,找了晚上睡覺的地點後,要先洗澡還是先找飯吃這件事吧! 有人說沐浴車要排隊2個小時太久,所以他的作法會是先吃飯,小休一下,等晚上九點多人少再去沐浴車;有的人則堅持要先洗完澡才能好好休息,休息完再東西即可。</p>
<p>當然這不會有標準答案的。</p>
<p>認真過頭,認真的人就會變成認真魔人。</p>
<p>另一個認真魔人的例子。是試著在隊伍中「維持秩序」的人。</p>
<p>在大隊人馬行進中,如果有車從後面來,通常會有人喊著「香燈腳靠右,後面有車」。然後人群便會慢慢分出一條通道,讓車經過;通過路口時,如果有車要右轉,會有熱心的香燈腳攔住隊伍,讓車子能順利左右轉。這些熱心的人,很常見。</p>
<p>但也有太認真的人,要求隊伍只能走在人行道跟慢車道,不斷地喊著,然後看著人群一會兒靠邊,一會兒又蔓延。然後…火氣就愈來愈大了。也許動機是好的,但作法和程度就有待商確了。不知道在他們的心裡,這百百種的香燈腳裡,是否也存在所謂的鄙視鏈?</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">香燈腳其實也是人間百態</h2>
<p>其實看了那麼多香燈腳的樣態,某一程度也反應了人間,反應了一樣米養百樣人 — 只是平均而言是偏善的一方,偏善的循環。在這個群體裡,包包放著去上廁所,可以安心;需要什麼提出來,有人會幫忙;善的循環持續著,至少在 3-sigma 裡是的。</p>
<p>我無法窮舉所有我看到的香燈腳,但我看到有很認真很認真對待的人,有懷著心只想去付出的人,有想管教其他人的人,也有想參加嘉年華的人。</p>
<p>走在這條路上,我看到一個又一個 不同面貌的靈魂,他們帶著各自的故事與期盼,匯聚成這條漫長的信仰之路。</p>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">系列文章閱讀:</h2>
<p>以下是關於2025我參加白沙屯媽祖進香的系列文章,供您快速一口氣閱讀:</p>
<ol class="wp-block-list">
<li><a href="https://blog.serv.idv.tw/2025/03/nomad-50-pilgrimage-registration/">遊民週記 50: 白沙屯媽祖進香 – 報名</a></li>
<li><a href="https://blog.serv.idv.tw/2025/04/pilgrimage-preparation/">白沙屯媽祖進香 – 準備</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-day1-day2-dnf/">白沙屯媽祖進香 – 去程急行軍</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-step-into-nantou/">白沙屯媽祖進香 – 見證歷史,走入名間</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/">白沙屯媽祖進香 – 台中大安的奇幻旅程</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-types-of-followers/">白沙屯媽祖進香 – 香燈腳眾生百態</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-other-thoughts/">白沙屯媽祖進香 – 其他心得</a></li>
</ol>
<p>如果你對這個主題有興趣,可以閱讀<a href="https://blog.serv.idv.tw/category/tour/baishatun-mazu/" target="_blank" rel="noreferrer noopener">同分類的文章</a>,或是點選相關的關鍵字,作更多的閱讀。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/05/pilgrimage-types-of-followers/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6340-scaled-e1747745554811.jpeg" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8172</post-id> </item>
<item>
<title>白沙屯媽祖進香 – 台中大安的奇幻旅程</title>
<link>https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/</link>
<comments>https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/#comments</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Sat, 17 May 2025 02:31:04 +0000</pubDate>
<category><![CDATA[白沙屯媽祖徒步進香 (2025)]]></category>
<category><![CDATA[大安]]></category>
<category><![CDATA[宗教]]></category>
<category><![CDATA[徒步]]></category>
<category><![CDATA[白沙屯媽祖]]></category>
<category><![CDATA[進香]]></category>
<category><![CDATA[露宿]]></category>
<category><![CDATA[靈性]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8158</guid>
<description><![CDATA[那是對我而言非常奇幻的一天。從早上報上馬、到晚上報下馬、再到隔天早上接著後續的行程, 兩天一夜,從起心動念,到 ... <a title="白沙屯媽祖進香 – 台中大安的奇幻旅程" class="read-more" href="https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/" aria-label="Read more about 白沙屯媽祖進香 – 台中大安的奇幻旅程">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>那是對我而言非常奇幻的一天。從早上報上馬、到晚上報下馬、再到隔天早上接著後續的行程,</p>
<p>兩天一夜,從起心動念,到發現媽祖應我所願、改變路線,賜我考驗;<br>在努力完成考驗後,發現回不去過夜地點的絕望;<br>在絕望時發現一線生機,然後平安無事地度過一夜。</p>
<p>從理性上來看,媽祖不會為你一個人的祈求或是心願改變數十萬大軍的路徑。<br>但就我個人的解讀之內,我仍然相信,這是媽祖賜給我,讓我無憾的一段奇幻體驗。</p>
<span id="more-8158"></span>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">那是 5/9 (五),回程第7天。</h2>
<p>前一天 (5/8) 媽祖停駕在彰化市和台中市的交界,福彰汽車,就在上大度橋之前一小段距離的地方。 我還記得,我是走在大度橋走到一半,看APP 才知道媽祖已經停駕,明日繼續。由於遊覽車停在橋的另一頭,所以我繼續把大度橋走完,上車,回香客大樓休息,沐浴、洗衣,就很普通的一夜。當天住的地方本來要連住兩天,但因為沒有曬衣服的地方又沒有空調,所以在整車香燈腳的抱怨之下,車掌要我們隔日把行李打包好,可能會更換住宿的地方。</p>
<p>我把洗好的衣服鋪在床板上,睡了四個小時。醒來後衣服當然沒乾,所以換上另一套 ,也就是去程急行軍的裝備 (九分壓力褲,走到小腿受傷那一套)。當時心裡還有一點陰影,但想一想,回程不是急行軍,又學會用自己速度走,應該不會受傷啦。至於沒乾的衣服,我就用衣架和夾子夾好,拿到遊覽車的位子上掛著。然後下車,開始今天的路程。</p>
<p>應該是走大度橋過去吧,我想。於是抓好媽祖起轎的時間,我往頭旗的方向,走上大度橋。</p>
<figure class="wp-block-image size-large is-style-default"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7234.jpeg?resize=1024%2C768&ssl=1" alt="" class="wp-image-8144" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7234-scaled.jpeg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7234-scaled.jpeg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7234-scaled.jpeg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7234-scaled.jpeg?resize=2048%2C1536&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7234-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<p>走到一半,有些人開始回頭。一問才知道,媽祖沒走上大度橋! (我都走兩次了…) 想一想,決定回頭。</p>
<p>從GPS 看起來,媽祖沿著堤防邊的道路走。問了路邊當地人,從一個涵洞穿過省道台1線,到對面後開始找小路走。半個小時後,終於接上香燈腳大軍…的後段。雖然離媽祖鑾駕大概一個小時,但這麼多天下來,我已經老神在在。用自己的速度走著,一邊享受田園之美和烏溪自行車道 (烏溪堤防水岸廊道) 的景觀。</p>
<div class="wp-block-jetpack-tiled-gallery is-style-rectangular"><div class=""><div class="tiled-gallery__gallery"><div class="tiled-gallery__row"><div class="tiled-gallery__col" style="flex-basis:63.97275%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7249-1024x768.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7249-1024x768.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7249-1024x768.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7249-1024x768.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7249-1024x768.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7249-1024x768.jpeg?strip=info&w=2000&ssl=1 2000w" alt="" data-height="1920" data-id="8145" data-link="https://blog.serv.idv.tw/img_7249/" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7249-1024x768.jpeg" data-width="2560" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7249-1024x768.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 1 張圖片,共 2 張圖片"/></figure></div><div class="tiled-gallery__col" style="flex-basis:36.02725%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7263-768x1024.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7263-768x1024.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7263-768x1024.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7263-768x1024.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7263-768x1024.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7263-768x1024.jpeg?strip=info&w=1920&ssl=1 1920w" alt="" data-height="2560" data-id="8148" data-link="https://blog.serv.idv.tw/img_7263/" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7263-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7263-768x1024.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 2 張圖片,共 2 張圖片"/></figure></div></div></div></div></div>
<div class="wp-block-jetpack-tiled-gallery aligncenter is-style-rectangular"><div class=""><div class="tiled-gallery__gallery"><div class="tiled-gallery__row"><div class="tiled-gallery__col" style="flex-basis:36.02725%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7283-768x1024.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7283-768x1024.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7283-768x1024.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7283-768x1024.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7283-768x1024.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7283-768x1024.jpeg?strip=info&w=1920&ssl=1 1920w" alt="" data-height="2560" data-id="8149" data-link="https://blog.serv.idv.tw/img_7283/" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7283-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7283-768x1024.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 1 張圖片,共 2 張圖片"/></figure></div><div class="tiled-gallery__col" style="flex-basis:63.97275%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7261-1024x768.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7261-1024x768.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7261-1024x768.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7261-1024x768.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7261-1024x768.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7261-1024x768.jpeg?strip=info&w=2000&ssl=1 2000w" alt="" data-height="1920" data-id="8147" data-link="https://blog.serv.idv.tw/img_7261/" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7261-1024x768.jpeg" data-width="2560" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7261-1024x768.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 2 張圖片,共 2 張圖片"/></figure></div></div></div></div></div>
<p>因為路線出乎眾人意料,所以其實沒什麼補給和點心攤。走著走著,開始出現騎機車趕過來提供茶水的善心人士,再來是一些小車開了進來,出現「累了請上車」,開始載運落後太遠的香燈腳。整個隊伍被拉得超長超長,完全沒有擠在一起的感覺。我先是選擇在堤防上走,後面為了補給和防曬,下到堤防邊走。那是一種很舒服,很安靜,真的像是在跟著媽祖走路的感覺 (雖然視線所及看不到鑾轎)。在過去、人不多、沒有進入城鎮受到熱烈的歡迎和補給時,進香,應該就是這樣的感覺吧,我想。</p>
<p>旁邊的人說,這樣走下去應該是和美吧,和美? 那不就有機會走上去程急行軍時,我放棄止步的那座伸港中彰大橋?</p>
<p>一念之間,難道,媽祖要給我一次機會,走上幾天前DNF時,放棄止步的那座橋嗎? 如果能這樣,那真的就是太好了,謝謝媽祖!</p>
<p>想著,走著,享受著,我真的往西走到了和美、伸港,接上了台17,然後走上了我來時放棄的那一座橋:中彰大橋。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7300.jpeg?resize=1024%2C768&ssl=1" alt="終於如願走上中彰大橋" class="wp-image-8151" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7300-scaled.jpeg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7300-scaled.jpeg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7300-scaled.jpeg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7300-scaled.jpeg?resize=2048%2C1536&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7300-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">終於如願走上中彰大橋 (烏溪)</figcaption></figure></div>
<p>「我猜…媽祖應該是要我把去程沒走的那一條台17中彰大橋,再好好的走過一次吧!這下大安溪、大甲溪、烏溪(大肚溪)、濁水溪 這幾條大河我都用雙腳走過了!」我心裡想著,感謝著,然後繼續用自己速度走著。</p>
<p>回程這一段路線跟來時好像,龍井、梧棲,但心情相差好多。去程正是中午時候,腿傷加上想睡,整個人意志在非常低落的狀態。回程也是接近中午,走在台61的另一側,但心情卻完全不同,好像是在郊遊一樣。再加上路邊居民和點心攤的熱烈支持,真的有點像是參加一場溫馨的慶典,那種暖意從心底蔓延開來。</p>
<p>在梧棲台灣大道 (以前叫台中港路?)往東,然後在大庄浩天宮跟童綜合醫院停駕休息之後,鑾轎走上台一線。已經下午三點多,今天已經走了33K,應該再走個一個小時,就會找地方駐駕了吧。會在清水嗎?還是?</p>
<p>結果沒有,鑾駕又繞回台61。繼續北上。</p>
<p>隊伍中開始有人在猜測,媽祖今天要去大甲 — 甚至有人說,要去大甲鎮瀾宮駐駕。我對於大甲多遠沒印象,跟著走就對了,我想。</p>
<p>下午五點多,還在清水台61 平面道路。LINE 群組裡遊覽車說他們在後面塞車,要開始收人回去,要我往回走。</p>
<p>喂!愈來愈過份了! 我想。現在連媽祖都還沒駐駕,也不是走在山路裡,你跟著台61開,這樣就要開始收人?!</p>
<p>牙一咬,「我要繼續跟媽祖走!」我回應。</p>
<p>『那你走到大甲再自己想辦法回到住宿點吧!還有兩個人也跟你一樣要繼續走,你們三個可以一起想辦法回來。』遊覽車小姐這麼回。</p>
<p>「媽祖,謝謝你幫我加課八九節!」我跟著隊伍走上大甲溪橋。天色昏暗,天氣涼爽,里程來到48K。一樣穿著九分壓縮褲,雙腿卻沒有任何的不適,這樣走下去,里程就快突破去程急行軍 DNF 的50K了。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7366.jpeg?resize=1024%2C768&ssl=1" alt="" class="wp-image-8153" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7366-scaled.jpeg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7366-scaled.jpeg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7366-scaled.jpeg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7366-scaled.jpeg?resize=2048%2C1536&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7366-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">繼續走過大甲溪橋,天氣涼爽。</figcaption></figure></div>
<p>想一想,第一天在意那個落後20分鐘、30分鐘、40分鐘等等,真的是很幼稚。記得那時候覺得,落後40分鐘就追不上了,怎麼辦? 但現在即使 GPS 顯示落後了一個小時,心裡還是穩穩踏實地,知道媽祖就在前方。</p>
<p>啊! 這是媽祖給我的補考! 我突然領悟。熱淚盈眶地。</p>
<p>49K、50K、51K、媽祖的路線沒有轉往大甲,而是繼續向北。我跟著,從點心攤拿了一個麵包,邊走邊啃著。<br>看著路邊開始有香燈腳坐著吃便當,這時候還不適合。我.繼續走著。</p>
<p>碰上被媽祖海放的頭旗,他們走得好快。用和他們一致的步伐和頻率,試著 match 急行軍的速度,一起走了一段。我.繼續走著。</p>
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="2025 白沙屯媽祖 - 頭旗 行進中" width="1162" height="654" src="https://www.youtube.com/embed/gW9Mowh17HU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
<p>下起大雨,用背包套把背包保護好。這次打包的方式對防水還蠻有效的,讓我碰上下雨時安心許多。我.繼續走著。</p>
<p>已經不想把GPS APP 拿出來看了。只要隊伍繼續前進,我就繼續走。走上61,再走下61,再走上61,再走下61。在某個交流道,人群塞住了,看過去,人群在61橋下往左延伸過去,但隊伍卻不太會動了。要駐駕了嗎?</p>
<p>看看手錶,55K ! 謝謝媽祖在回程時給我一次補考的機會!我這次,應該算是通過補考了吧。一個半路上閃過的念頭和小小的祈願,讓我不但走過了來時止步的中彰大橋,還穿著一樣的裝備,重新補考了來時急行軍的50K。</p>
<p>淋著雨的我,誠心地感謝媽祖給我這一段奇幻的旅程。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">到了大安,然後呢?</h2>
<p>在61陸橋下稍作停歇,反正隊伍也不動了。我開始審視自己接下來的計畫。</p>
<p>手機網路塞爆,離線GPS 告訴我身在一個大安的小漁村,許多人在61高架橋下躲著雨,兩三台車趕來發著晚餐 — 有白飯和配菜。自己拿碗裝。 但當下的我,沒有心情好好吃一頓飯。</p>
<p>今天的奇幻旅程已經夠我好好回味。回到理智的我,應該要想辦法回去住宿點。在這之前,我要做的,是先跟另外兩位也要走到大甲的團員會合。</p>
<p>理想狀況是,我們三人會合後,再想辦法回到住宿點 (搭車?/包車?)。<br>現實情況是,網路塞爆,電話也撥不出去;這是一個小漁村,沒有對外的大眾運輸;如果要去大甲,步行要一個半小時;累了請上車目前都還是載人過來,還沒辦法利用。</p>
<p>聽說媽祖在不遠處已經駐駕。(聽說的,手機完全不能用)。跟媽祖報個下馬後,我決定先往反方向 (東邊)走,走一小段,找個民宅的大門下,一邊躲雨,一邊用手機連絡那兩位團員。原來他們中途就找地方躲雨,所以位置在隊伍更後方一些。他們傳訊息要我不要移動,他們會過來會合。然而,等他們一到陸橋附近,他們就收不到我的訊息了 (我傳得出去,但他們沒有已讀)。</p>
<p>最後一則訊息,是他們問「要一起叫 Uber 嗎?」</p>
<p>自此,我再也連絡不上他們。</p>
<p>看起來,奇幻旅程即使告一段落,魔幻的一天還在繼續。</p>
<p>只剩我自己了。</p>
<p>ps. 跟我無法會合的團員,聽說當晚有想辦法回到住宿地。但怎麼回去的,不知道。因為我再也沒有跟他們碰到面。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">不只補考,還有加考</h2>
<p>當下想到幾個方案:</p>
<ol class="wp-block-list">
<li>想辦法先到大甲市區,再搭車 (?) 到住宿點 (伸港福安宮) : 但伸港到大甲沒有什麼大眾運輸工具。</li>
<li>想辦法到大甲市區,找個地方過一晚 (旅社之類的):走路應該不太可行,要靠「累了請上車」。後來的確有幾台是到大甲市區的。隔天早上要再想辦法從大甲市區回來繼續走。</li>
<li>往東走,在30分鐘的步行路程內找到可以過夜的地方。</li>
<li>在陸橋下過夜。 (我看已經有人搭好帳篷了)</li>
<li>往西走,去媽祖駐駕的地方過夜。</li>
</ol>
<p>人潮愈來愈多,一方面是隊伍後方的香燈腳們陸續到達,許多人都塞在閘道出口這邊;一方面是附近的居民收到消息,趕來媽祖駐駕的地點參拜,汽機車把小路擠的水洩不通;還有陸續下閘道的「累了請上車」跟遊覽車 (為什麼別的車就跟來了,我們車早早就放生了?),整個高架橋下是滿滿的人、滿滿的車。</p>
<p>天氣微涼,雨變小了,附近的民家可以借宿的應該都被借得差不多了。看著高架橋下開始占地準備過夜的人們,我決定試試在陸橋下過夜的選項。</p>
<p>第一步:找紙箱當床。</p>
<p>一個熱心的大哥聽到我在詢問紙箱,豪爽地跟我說:「免啦!你往那邊走,小心地越過那群在橋下紮營的人們,再過去一個 block,有一個區域很昏暗,但地上是鋪了防水布的。好像還有一些位置,不用紙箱啦!!」</p>
<p>順著他指的方向,我在高架橋下摸索地走,小心地繞過那些帳篷和已經有人鋪地墊的區域,順利地找到他所說的地點。頭頂是高架橋,不怕淋雨,兩側有半人高的牆,風應該不會直接灌進來。地上鋪著防水布,表面有點沙,但這是海邊,正常。看著人群一個個找了自己的位置,我看到一個空位,把背包放下。這 — 就是我今晚的住宿點。</p>
<p>我即將解鎖街友成就 <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f61b.png" alt="😛" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<div class="wp-block-jetpack-tiled-gallery aligncenter is-style-rectangular"><div class=""><div class="tiled-gallery__gallery"><div class="tiled-gallery__row"><div class="tiled-gallery__col" style="flex-basis:36.02725%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7373-768x1024.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7373-768x1024.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7373-768x1024.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7373-768x1024.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7373-768x1024.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7373-768x1024.jpeg?strip=info&w=1920&ssl=1 1920w" alt="" data-height="2560" data-id="8154" data-link="https://blog.serv.idv.tw/img_7373/" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7373-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7373-768x1024.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 1 張圖片,共 2 張圖片"/></figure></div><div class="tiled-gallery__col" style="flex-basis:63.97275%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7380-1024x768.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7380-1024x768.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7380-1024x768.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7380-1024x768.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7380-1024x768.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7380-1024x768.jpeg?strip=info&w=2000&ssl=1 2000w" alt="" data-height="1920" data-id="8157" data-link="https://blog.serv.idv.tw/img_7380/" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7380-1024x768.jpeg" data-width="2560" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7380-1024x768.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 2 張圖片,共 2 張圖片"/></figure></div></div></div></div></div>
<div class="wp-block-jetpack-tiled-gallery aligncenter is-style-rectangular"><div class=""><div class="tiled-gallery__gallery"><div class="tiled-gallery__row"><div class="tiled-gallery__col" style="flex-basis:63.97275%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7377-1024x768.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7377-1024x768.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7377-1024x768.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7377-1024x768.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7377-1024x768.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7377-1024x768.jpeg?strip=info&w=2000&ssl=1 2000w" alt="" data-height="1920" data-id="8155" data-link="https://blog.serv.idv.tw/img_7377/" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7377-1024x768.jpeg" data-width="2560" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7377-1024x768.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 1 張圖片,共 2 張圖片"/></figure></div><div class="tiled-gallery__col" style="flex-basis:36.02725%"><figure class="tiled-gallery__item"><img decoding="async" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7378-768x1024.jpeg?strip=info&w=600&ssl=1 600w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7378-768x1024.jpeg?strip=info&w=900&ssl=1 900w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7378-768x1024.jpeg?strip=info&w=1200&ssl=1 1200w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7378-768x1024.jpeg?strip=info&w=1500&ssl=1 1500w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7378-768x1024.jpeg?strip=info&w=1800&ssl=1 1800w,https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7378-768x1024.jpeg?strip=info&w=1920&ssl=1 1920w" alt="" data-height="2560" data-id="8156" data-link="https://blog.serv.idv.tw/img_7378/" data-url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7378-768x1024.jpeg" data-width="1920" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7378-768x1024.jpeg?ssl=1" data-amp-layout="responsive" aria-label="以全螢幕開啟第 2 張圖片,共 2 張圖片"/></figure></div></div></div></div></div>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">露宿高架橋下</h2>
<p>先傳了個訊息告訴車掌我今晚不回去了,雖然他也不會關心我有沒有回去。然後開始解決過夜要做的事:</p>
<ul class="wp-block-list">
<li>食物和飲水:剛才的餐車已經空了。但背包還有乾糧,包括能量棒。今晚沒問題。水的話把身邊這一瓶喝完後,明早一定要找地方補水。</li>
<li>衣服和盥洗:今天穿的九分壓縮褲,就當長褲吧。把溼掉的鞋襪脫掉鋪好,希望一個晚上後可以變乾。沒有的話也沒關係,明天早上穿上後,應該可以靠身體的熱度變乾。背包裡還有一件薄外套,正好穿在跑衣外,這樣就有長袖長褲了。溼掉的進香背心則是一樣脫掉鋪在一旁。今晚就不洗澡了。拿出溼紙巾把臉跟脖子擦一擦,就完成今天的盥洗。廁所的話問到在駐駕餐廳後方的民宅有外借,所以去排了隊借了廁所。</li>
<li>床位和寢具:防水帆布上我加鋪了攤開的便利雨衣,雖然是水泥地板,但累的時候怎樣都睡得著。背包把裡頭裝的東西稍作整理後對折,拿後背柔軟的區域當作枕頭。戴上耳塞當作基本的隔音,可以把高架橋上車子經過伸縮縫的聲音濾掉大部份;人聲的部份現場香燈腳們在休息時其實都蠻安靜的,打呼聲應該是唯一會聽到(且擾人)的聲音。</li>
<li>通訊和充電:現代人沒有手機、沒有充電應該會出現嚴重的焦慮,我也是。環顧現場,橋下是沒有插頭的 (廢話)。附近有唯一一台充電車,老闆說最後一桶油,用完他就沒辦法服務大家了。我看一群人擠著他的小發財,占據了排插上每一個充電孔。我自己的行動電源一顆用完,還剩一顆 (當初背包裡放兩顆真是太幸運了),而且明天早上如果碰到遊覽車 (他們總要把人載過來吧!),我的過夜行李還有一顆!想一想,心安許多。</li>
</ul>
<p>這樣子盤點起來,今晚的過夜好像不是這麼可怕了。</p>
<p>反正沒有網路,手機的電也要省著用,人也累了,雖然隔壁床友還躺著滑手機,但我決定耳塞一塞,眼睛一閉,沈沈睡去。</p>
<p>半夜兩點,刮起風來,我被冷醒。身上的衣服當然還沒乾,風一吹,有種失溫的感覺。決定把鋪地的便利雨衣穿在身上,半溼半乾的襪子也穿在腳上,整個腿縮進便利雨衣內,發抖著。然後找出背包裡的巧克力燕麥棒,一口咬下。後面還補了鹽糖,這才慢慢不發抖了。保持屈膝側躺的姿勢,我再次沈沈睡去。</p>
<p>四點半,被鬧鐘叫醒。身邊有些人已經開始打包行李準備出發。我也開始整理裝備。襪子和鞋子已經乾到勉強到可以穿了。身上這麼薄的便利雨衣竟然沒有破。懷著感恩的心把便利雨衣折好,放進背包。看著旁邊專業的床友窩在防雨披風裡,只露出一顆頭,人家真的是專業。</p>
<p>起床後,我開始看到一台台遊覽車從61的閘道開下,往駐駕地前進。放下香燈腳,繼續開走。本來擔心遊覽車又開上61,這樣我就沒辦法上車整理行李了。還好車子沒開上61,而是往前開到海墘社區附近,那邊靠堤防邊可以停車。我看著我所屬的那台遊覽車開下交流道,就連絡上車掌,上了車,把過夜行李收好。換上新的行動電源,我又下車了。</p>
<p>經歷過昨天55K的奇幻旅程,再加上在高架橋下露宿一夜,身上竟然沒有太多疲憊的感覺,測了一下雙腿的反應也十分正常,反而有種滿血回歸的興奮感。唯一的缺點是衣服的味道,但那是別人聞得到,自己是聞不到的 XD</p>
<p>帶著這傳說中「被媽祖加持」的 buff ,我繼續用自己走路的方式 (已經很熟練了),邁向另一天的開始,今天,將會從大安走到通霄…(那一天,又走了32K… )</p>
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="2025 白沙屯媽祖 - 海墘社區" width="1162" height="654" src="https://www.youtube.com/embed/ZkQQwwLQqg4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">事後來看</h2>
<p>說是因緣也可以,說是巧合也可以:</p>
<ul class="wp-block-list">
<li>因為前一個住宿點沒辦法曬衣服,而穿九分褲,這樣在露宿的時候才有長袖長褲。</li>
<li>因為前一個住宿點沒有冷氣,所以車掌叫我們全部行李打包上車,我的家當才不會被遺留在香客大樓。</li>
<li>因為鑾轎不走大度橋,我才有機會走上去程止步的中彰大橋。</li>
<li>因為一直走到大安,我才有機會補考,完成無痛無傷的55K。</li>
<li>因為大安實在太偏僻,我才有機會在外面露宿,體驗不靠遊覽車接送住宿的生活。</li>
<li>因為一位大哥的指點,我才有機會在身上完全沒有過夜裝備的情況下,找到適合的鋪位,並安全度過一夜。</li>
<li>因為多帶了一顆行動電源,隔天我才能在不停下來充電的情況下,繼續跟著媽祖前行。</li>
</ul>
<p>有時候,讓自己沈浸在那奇幻/魔幻的感受中就夠了。當你在烈日下走到昏頭時,一念祈求,一股涼風迎來,謝謝媽祖;當你走到腳板隱隱作痛時,一念祈求,隊伍停了下來,媽祖停駕了,謝謝媽祖;當你擔心不知如何過夜時,一念祈求,一位大哥指點了你最適合你身上裝備的容身之處,謝謝媽祖。</p>
<p>可以歸咎在巧合,可以歸咎在附會或強解,但也可以說,那是一種細心感受、學習感謝的過程。那是一個科學和工程的信徒,在這麼多年人生中少有的體驗。我以前不太能理解,許多受尊敬的科學家和工程專家,本身也是虔誠的天主或基督教徒,這兩者之間,不會衝突嗎?</p>
<p>經過自己的體驗後,科學/工程 和 神學/玄學/靈性,在個人的理解和建構體系裡,已經不會這麼的對立或分離了。當然,這只是我第一年的體驗,未來的路,還遠著呢。</p>
<p>我並沒有發什麼大願,但明年,我應該還是會走在跟著媽祖進香的路上。</p>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">系列文章閱讀:</h2>
<p>以下是關於2025我參加白沙屯媽祖進香的系列文章,供您快速一口氣閱讀:</p>
<ol class="wp-block-list">
<li><a href="https://blog.serv.idv.tw/2025/03/nomad-50-pilgrimage-registration/">遊民週記 50: 白沙屯媽祖進香 – 報名</a></li>
<li><a href="https://blog.serv.idv.tw/2025/04/pilgrimage-preparation/">白沙屯媽祖進香 – 準備</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-day1-day2-dnf/">白沙屯媽祖進香 – 去程急行軍</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-step-into-nantou/">白沙屯媽祖進香 – 見證歷史,走入名間</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/">白沙屯媽祖進香 – 台中大安的奇幻旅程</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-types-of-followers/">白沙屯媽祖進香 – 香燈腳眾生百態</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-other-thoughts/">白沙屯媽祖進香 – 其他心得</a></li>
</ol>
<p>如果你對這個主題有興趣,可以閱讀<a href="https://blog.serv.idv.tw/category/tour/baishatun-mazu/" target="_blank" rel="noreferrer noopener">同分類的文章</a>,或是點選相關的關鍵字,作更多的閱讀。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/feed/</wfw:commentRss>
<slash:comments>1</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_7212-scaled-e1747445829727.jpeg" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8158</post-id> </item>
<item>
<title>白沙屯媽祖進香 – 見證歷史,走入名間</title>
<link>https://blog.serv.idv.tw/2025/05/pilgrimage-step-into-nantou/</link>
<comments>https://blog.serv.idv.tw/2025/05/pilgrimage-step-into-nantou/#respond</comments>
<dc:creator><![CDATA[PipperL]]></dc:creator>
<pubDate>Thu, 15 May 2025 01:05:00 +0000</pubDate>
<category><![CDATA[白沙屯媽祖徒步進香 (2025)]]></category>
<category><![CDATA[南投]]></category>
<category><![CDATA[名間]]></category>
<category><![CDATA[步行]]></category>
<category><![CDATA[白沙屯媽祖]]></category>
<category><![CDATA[進香]]></category>
<guid isPermaLink="false">https://blog.serv.idv.tw/?p=8123</guid>
<description><![CDATA[2025/5/6 星期二,白沙屯媽祖回鑾第 3 天。出乎意料,在往年常去的苗栗、台中、彰化、雲林四個縣市之外, ... <a title="白沙屯媽祖進香 – 見證歷史,走入名間" class="read-more" href="https://blog.serv.idv.tw/2025/05/pilgrimage-step-into-nantou/" aria-label="Read more about 白沙屯媽祖進香 – 見證歷史,走入名間">閱讀全文</a>]]></description>
<content:encoded><![CDATA[
<p>2025/5/6 星期二,白沙屯媽祖回鑾第 3 天。出乎意料,在往年常去的苗栗、台中、彰化、雲林四個縣市之外,<br>媽祖的隊伍走進了南投,前往名間鄉。</p>
<p>大部份的報導,都稱之為200多年來首進南投。但也有人說那是「有路線紀錄」的頭一遭。在早期也可能已經造訪過南投。</p>
<p>無論如何,這意外的驚喜都讓今年的香燈腳們多了許多話題。而我,很開心地在參加的第一年,就有機會見證這麼歷史性的一刻,跟隨媽祖的腳步,走入南投名間鄉。</p>
<span id="more-8123"></span>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">早安石榴,壯觀林內</h2>
<p>早上遊覽車把我們放在斗六交流道附近,時間抓一下,大概往回走20分鐘可以回到媽祖駐駕的地方。我往回走了一段路,看時間差不多了,就在路邊吃了一碗麵線糊、喝了一碗綜合湯,等待媽祖鑾轎經過。</p>
<p>一如我<a href="https://blog.serv.idv.tw/2025/05/pilgrimage-day1-day2-dnf/">前面提到的走法</a>,我等待媽祖走過後20分鐘,隊伍不那麼擁擠後,才加入人群。依這個方向,下一個城鎮會是林內。</p>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6752.jpeg?resize=1024%2C768&ssl=1" alt="" class="wp-image-8124" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6752-scaled.jpeg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6752-scaled.jpeg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6752-scaled.jpeg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6752-scaled.jpeg?resize=2048%2C1536&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6752-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<p>在路上喝了一杯手沖咖啡,已經是每天的儀式了。早上出發之後,都會祈願著「媽祖我想喝咖啡」。然後不久後,媽祖就會賜我咖啡。前幾天我執著要在遇見的第一個咖啡攤等待,但有時排隊的人眾多,老闆一個人手沖來不及。後來我索性「隨緣」,如果排太多人,就但表媽祖暗示我時機未到,繼續前行。而今天跟我結緣的咖啡,名叫「歡喜煮」,多線平行處理的產能讓我不怎麼等候,就能喝到一杯咖啡,非常感恩。</p>
<p>走過斗六東溪,我們開始進入林內市區。趁著媽祖停駕在消防局林內分隊,我慢慢超前,在消防隊旁讓雙腿休息一下,然後走到隊伍的前方,到台3線 (外環道) 和中正路 (進入林內市區) 的交界處附近等候,等待鑾轎擇路。</p>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1162.5" height="1163" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=1162.5%2C1163&ssl=1" alt="" class="wp-image-8127" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?w=2560&ssl=1 2560w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=400%2C400&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=1024%2C1024&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=150%2C150&ssl=1 150w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=1536%2C1536&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=2048%2C2048&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?resize=120%2C120&ssl=1 120w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6788-edited-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1162px) 100vw, 1162px" /></figure>
<p>當媽祖選擇了進入林內市區,大批的香燈腳們把本就不寬的市區道路給擠爆了,四線道所有的車都動彈不得,不久後放眼望去,只剩橘帽大軍,非常壯觀。在林內車站停駕的時候,我找了個小巷子彎進去休息,跟旁邊的香燈腳大哥們聊著,看著對面的香燈腳和當地的狗狗玩著,聽著林內國小的孩子們大喊「謝謝媽祖!!!」,再到附近的早餐店借個洗手間,早上的時間,悠遊自在。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">竹山或二水,還是…名間?</h2>
<p>離開林內,下一個路口要決定往南投竹山或彰化二水。</p>
<p>聽其他的香燈腳說,南投市長今年有獲得3聖筊,所以很有機會媽祖會走竹山。這裡因為人潮實在太多了,而且媽祖好像選了蠻久的 (超過10分鐘) ,所以即使我在隊伍20分鐘後出發,依舊被卡在人群中動彈不得。等到人群開始騷動 — 媽祖沒有選竹山! 而是往二水方向過彰雲大橋。</p>
<p>「那就是 二水 、田中、然後往北了」一個在我旁邊的香燈腳大哥這麼說。</p>
<p>有經驗的香燈腳們,閒聊時的話題往往下一段路線的預判,包括可能去的城鎮,過去歷史的路線,沿途可能駐駕的宮廟學校機關等等。第一次參加的我,就在一旁聽,一邊補充我對這附近地方貧乏的地理和人文知識。</p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="768" height="1024" data-id="8128" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6803.jpeg?resize=768%2C1024&ssl=1" alt="" class="wp-image-8128" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6803-scaled.jpeg?resize=768%2C1024&ssl=1 768w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6803-scaled.jpeg?resize=300%2C400&ssl=1 300w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6803-scaled.jpeg?resize=1152%2C1536&ssl=1 1152w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6803-scaled.jpeg?resize=1536%2C2048&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6803-scaled.jpeg?w=1920&ssl=1 1920w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="768" height="1024" data-id="8129" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6807.jpeg?resize=768%2C1024&ssl=1" alt="" class="wp-image-8129" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6807-scaled.jpeg?resize=768%2C1024&ssl=1 768w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6807-scaled.jpeg?resize=300%2C400&ssl=1 300w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6807-scaled.jpeg?resize=1152%2C1536&ssl=1 1152w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6807-scaled.jpeg?resize=1536%2C2048&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6807-scaled.jpeg?w=1920&ssl=1 1920w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" data-id="8130" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6815.jpeg?resize=1024%2C768&ssl=1" alt="" class="wp-image-8130" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6815-scaled.jpeg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6815-scaled.jpeg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6815-scaled.jpeg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6815-scaled.jpeg?resize=2048%2C1536&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6815-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</figure>
<p></p>
<p>走在彰雲大橋上,算是跨過濁水溪了。10點的天氣已經炎熱,藍天、白雲、另一頭就是彰化二水、以及相連的田中。想到二水馬跟田中馬跑在八堡圳旁,不曉得這次的路線,會不會經過馬拉線路線熟悉之處?</p>
<p>我傳了個訊息給在田中的友人,跟他說,也許今天有機會在田中相見。享受除了田中馬之外,田中人迎媽祖的熱情!</p>
<p>過了彰雲大橋,隊伍沒有如預期地左轉往二水市區,而是右轉,隊伍開始有人討論「這是要往名間的路!」我對這裡不熟,唯一的線索就是路邊的自行車道指示牌「往名間10.1K,步行時間2時14分」。難道說,我要見證歷史了嗎?</p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="768" data-id="8131" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6820.jpeg?resize=1024%2C768&ssl=1" alt="" class="wp-image-8131" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6820-scaled.jpeg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6820-scaled.jpeg?resize=400%2C300&ssl=1 400w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6820-scaled.jpeg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6820-scaled.jpeg?resize=2048%2C1536&ssl=1 2048w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6820-scaled.jpeg?w=2325&ssl=1 2325w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="768" height="1024" data-id="8132" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6828.jpeg?resize=768%2C1024&ssl=1" alt="" class="wp-image-8132" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6828-scaled.jpeg?resize=768%2C1024&ssl=1 768w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6828-scaled.jpeg?resize=300%2C400&ssl=1 300w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6828-scaled.jpeg?resize=1152%2C1536&ssl=1 1152w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6828-scaled.jpeg?resize=1536%2C2048&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6828-scaled.jpeg?w=1920&ssl=1 1920w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>
<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="768" height="1024" data-id="8133" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6834.jpeg?resize=768%2C1024&ssl=1" alt="" class="wp-image-8133" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6834-scaled.jpeg?resize=768%2C1024&ssl=1 768w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6834-scaled.jpeg?resize=300%2C400&ssl=1 300w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6834-scaled.jpeg?resize=1152%2C1536&ssl=1 1152w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6834-scaled.jpeg?resize=1536%2C2048&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6834-scaled.jpeg?w=1920&ssl=1 1920w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>
</figure>
<p>天氣愈來愈炎熱了,在路旁休息的人很多,我和鑾轎的距離也很遠。不過心裡已經沒有急行軍那天的焦急,我不用追媽祖,只要跟著媽祖的腳步走就好。走過引水公園,走過八堡圳公園,走過領便當的人群,路線來到山路。</p>
<p>預期外的香燈腳大軍,讓員集路(152縣道)動彈不得,雙向車道都塞滿了車,步行的人群則走在邊線上。跨過縣界的告示牌,我們來到南投縣名間鄉,見證歷史新的一頁。200多年來,白沙屯媽祖進香,歷史性的一刻,走入南投。</p>
<p>這裡手機的訊號基本上是零格的,不是網路癱瘓,而是訊號本來就是零格。所以也沒辦法知道媽祖鑾駕的位置或動態。但在香燈腳的口耳相傳之下,得知目前停駕在30~60分鐘路程的修玄宮中,而且會休息到下午3點。這意味著我可以找個地方午餐並午休了。我先走到靈山禪寺中,想找個空地休息,很幸運地找到一張椅子。和附近的香燈腳們聊了一下,然後各自歇息。(反正也沒有手機可以滑)</p>
<p>一點左右,我決定去找些吃的。一般來說,媽祖中午停駕的地方會有餐車發放便當或自助餐。不過我走了沒多久,就在路邊被塞了一個大雞腿便當。</p>
<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" width="768" height="1024" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6859.jpeg?resize=768%2C1024&ssl=1" alt="" class="wp-image-8134" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6859-scaled.jpeg?resize=768%2C1024&ssl=1 768w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6859-scaled.jpeg?resize=300%2C400&ssl=1 300w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6859-scaled.jpeg?resize=1152%2C1536&ssl=1 1152w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6859-scaled.jpeg?resize=1536%2C2048&ssl=1 1536w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6859-scaled.jpeg?w=1920&ssl=1 1920w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure></div>
<p>一樣,我找了條路邊的小路,在香蕉園旁把這雞腿便當解決掉,然後繼續前往,往修玄宮前進。原來修玄宮也是天上聖母 (媽祖),參拜的人潮實在太多,有許多是名間的居民聽聞消息,趕來參拜的。我繞了一圈,決定先往前走。因為只有一條,所以應該可以走到接近名間市區的地方,也就是濁水火車站附近。</p>
<p>人實在太多,但慢慢前行,還是可以的。在火車站附近找了個民它,有開放庭院供香燈腳歇息,地上還貼心地鋪了帆布。跟著進去找了塊自己的空地,脫了鞋襪,聽著後方幾位資深的香燈腳們,爭論著晚上過夜找到鋪位後、要先洗澡還是先吃飯。都是資深的,沒有對錯;另一邊則是幾個「一日遊」的,討論的話題又完全不同。香燈腳百百款,有機會再開一篇聊。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">名間往受天宮,殘念。</h2>
<p>起駕的時刻,我躲到市區的小巷內,看著GPS APP 的媽祖動向 (總算有訊號了),打算等祂確定走向後,再跟過去。往北是往南投市方向;往南是竹山方向 (機率較低)。因為人太多,路太小,前進的速度很慢。今天休息的時間好久,心裡有些煩燥。</p>
<p>等到旁邊的香燈腳們開始討論起來,打開GPS ,發現鑾轎竟然避開市區台3線…往山上走去。查了一下,那條路是往松柏嶺受天宮,聽在地的香燈腳說,帝爺公那邊停車場很大,有過夜的地方,不過上山爬坡,而且又是山路。</p>
<p>原來媽祖要我休息這麼久是因為這緣故!</p>
<p>起身準備跟隨,突然想到,那遊覽車呢?遊覽車目前還塞在往名間的路上,如果一樣是縣道山路,後面遊覽車就算塞上去了,怎麼下山載我們去香客大樓?</p>
<p>看了一下 LINE 群組。果然,遊覽車沒有要上山,強烈要求我們在台3線上車。嘴上說會等到媽祖駐駕再開始收人,載去過夜的地方,實際上才下午4、5點,就要我們在半途上車「配合趕去住宿的地方,不然廟門要關了」。這種現象在後來的幾天愈來愈誇張,有位同車的大哥乾脆就不理他,自己找地方過夜了;另一組同車的香燈腳則是繼續前進,等駐駕後再自己搭車/叫車到住宿的地方會合。而我,也在大安的那一夜因此露宿街頭。</p>
<p>但往受天宮的那一天,我在判斷後決定在台3線與其他同車的會合,等待遊覽車慢慢開到名間,接我們上車。</p>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-recalc-dims="1" loading="lazy" decoding="async" width="720" height="727" src="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6901.jpeg?resize=720%2C727&ssl=1" alt="" class="wp-image-8136" srcset="https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6901.jpeg?w=720&ssl=1 720w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6901.jpeg?resize=396%2C400&ssl=1 396w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6901.jpeg?resize=150%2C150&ssl=1 150w, https://i0.wp.com/blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6901.jpeg?resize=120%2C120&ssl=1 120w" sizes="auto, (max-width: 720px) 100vw, 720px" /></figure></div>
<p>後續看報導,受天宮也破例開廟門讓媽禮鑾轎入殿,兩大神明相見歡;當天受天宮廟區還不關大門,連夜從名間市區往受天宮的交通都是堵塞的,大批名間的居民上山參拜,絡繹不絕。</p>
<p>而我,雖然錯過了上山山路這一段,但一整天的經歷還是有見識歷史一刻的感動。</p>
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="2025 白沙屯媽祖 - 受天宮起駕" width="1162" height="654" src="https://www.youtube.com/embed/qJNmhnu8-5o?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
<p>等我踏上松柏嶺,已經是隔天了。很幸運地紀錄到起轎的畫面,並且專心地享受接下來的旅程 — 下山山路、雲霧飄渺、雨中急行等。</p>
<p>對了!後來媽祖還是帶我們去田中了!</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">後記:</h2>
<p>隔天看著遊覽車載我們上山的路徑,其實他們是有其他路可以上松柏嶺的,只是他們想早點休息而已。我們在香客大樓住宿時,有一車跟著上山的遊覽車,大概晚我們3個小時到。也就是說,其實還有其他車是跟上山的,要不要做而已。</p>
<p>當然,我不知道跟上山的遊覽車,車上的香燈腳們心情是作何感想?是能一起見證歷史而開心?還是因為不能早點去香客大樓而喪氣? 香燈腳百百款,像我這種,一定秒選「跟上山」。</p>
<p></p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
<h2 class="wp-block-heading">系列文章閱讀:</h2>
<p>以下是關於2025我參加白沙屯媽祖進香的系列文章,供您快速一口氣閱讀:</p>
<ol class="wp-block-list">
<li><a href="https://blog.serv.idv.tw/2025/03/nomad-50-pilgrimage-registration/">遊民週記 50: 白沙屯媽祖進香 – 報名</a></li>
<li><a href="https://blog.serv.idv.tw/2025/04/pilgrimage-preparation/">白沙屯媽祖進香 – 準備</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-day1-day2-dnf/">白沙屯媽祖進香 – 去程急行軍</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-step-into-nantou/">白沙屯媽祖進香 – 見證歷史,走入名間</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-magic-experience/">白沙屯媽祖進香 – 台中大安的奇幻旅程</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-types-of-followers/">白沙屯媽祖進香 – 香燈腳眾生百態</a></li>
<li><a href="https://blog.serv.idv.tw/2025/05/pilgrimage-other-thoughts/">白沙屯媽祖進香 – 其他心得</a></li>
</ol>
<p>如果你對這個主題有興趣,可以閱讀<a href="https://blog.serv.idv.tw/category/tour/baishatun-mazu/" target="_blank" rel="noreferrer noopener">同分類的文章</a>,或是點選相關的關鍵字,作更多的閱讀。</p>
<hr class="wp-block-separator has-alpha-channel-opacity"/>
]]></content:encoded>
<wfw:commentRss>https://blog.serv.idv.tw/2025/05/pilgrimage-step-into-nantou/feed/</wfw:commentRss>
<slash:comments>0</slash:comments>
<media:content url="https://blog.serv.idv.tw/wp-content/uploads/2025/05/IMG_6820-1-scaled-e1747191004319.jpeg" medium="image"></media:content>
<post-id xmlns="com-wordpress:feed-additions:1">8123</post-id> </item>
</channel>
</rss>
<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/
Page Caching using Disk
Minified using Disk
Database Caching using Disk (Request-wide modification query)
Served from: blog.serv.idv.tw @ 2025-07-07 05:02:27 by W3 Total Cache
-->