寫爬蟲絕對不要太客氣也不可以心軟

尤其是要爬像是 Facebook 這種蓄意想要鎖住資訊,把 Internet 變成一個巨大內網的邪惡公司的時候。

之前在 Github 看到 rss-bridge 這個專案,用爬蟲去爬網頁然後產生 RSS/Atom feed ,原本是想要把它用在 Facebook 上面,這樣就可以訂閱粉專的每一篇貼文、照時間排序、不用被 Facebook 的 filter bubble 審查資訊,也不會因為你訂閱什麼就汙染你的 stream 。

架起來試了一下發現效果還不錯,訂閱了一堆粉專就去睡了。

結果隔天早上醒來發現所有 feed 都失效,因為 Facebook 要求 Captcha 驗證。原本的 rss-bridge 會把驗證圖片 proxy 顯示給瀏覽 feed 的使用者,使用者可以解完之後送出,rss-bridge 會再送給 Facebook ,但這功能實測的時候發現無論如何送出都會失敗,把這 bug 解掉之後又發現 captcha 只會當次有效,也就是要發另一個 request 的時候又會被跳 captcha 。

自己開瀏覽器測試了一下發現,瀏覽器的 captcha 只要解一次,未來的 request 就都會過,想說大概是因為 rss-bridge 的爬蟲不支援 cookie 的問題,所以就花了好幾天時間把 rss-bridge 的爬蟲換成 PHP curl (原本是用 file_get_contents() ),因為 PHP curl 有 cookie jar 的功能,管理 cookie 比較方便。

結果新的 code 終於會動,但儘管送了 cookie 還是每次被跳 captcha ,挖到底層去測試了很多東西,檢查 request header 和 response header ,確定不是我實作有問題之後,我發現問題不在 cookie。

問題在於 rss-bridge 的爬蟲預設都會多送一個 GET 參數 _fb_noscript=1 ,有這個參數的時候顯示出來的 captcha 會不一樣!

_fb_noscript=1 的時候顯示的 captcha 會直接是一個 <img src="..."> ,而且這個 captcha 也只有單次有效!下次再請求,就算有 cookie, 也會再被要求解另一個 captcha 。

沒有那個參數的時候的 captcha 圖片會使用 AJAX 載入,然後解開之後設定的 cookie 也會多次有效。

我拿舊的實作改成 curl 的時候沒有特別改這個東西,結果到現在才發現這件事。

所以,這次學到的教訓是,寫爬蟲的時候,不要仁慈,直接開 PhantomJS/CasperJS 吧……

P.S. 我改過的 rss-bridge 在這兒: https://github.com/pellaeon/rss-bridge/tree/enhance

更新:不想用 Javascript 的話 Python 的 Scrapy + Splash (Splash 是 headless browser)看起來還不錯(沒用過)。還有 django-dynamic-scraper 可以直接把爬到的東西結構化成 Django model object ,每一個 field 直接關聯一個 selector ,scrape 的時候讀進去,太神啦。

2017/2/24 更新2:後來又繼續試著解決這個問題。研究了一下 django-dynamic-scraper , 幾乎找不到如何處理 captcha 的說明和範例,雖然說應該是可以解但是感覺又要研究 scrapy 很久,所以還是決定回去幫 rss-bridge 加上 proxy 的功能。改出來的東西在 https://github.com/pellaeon/rss-bridge/tree/enhance2 。然後還花了一些心力研究要怎麼取得免費的 proxy 清單,最後發現 HideMyAss.com 的不錯,寫了個小小的 Javascript 去撈 proxy 清單

計劃太多,徵人(?)

想做的專案太多,時間能力不夠,只好先把想法記下來。更希望可以找到人一起來合作一個專案啦,自己做太容易放棄了。

owncloud-registration

讓 ownCloud/Nextcloud 支援註冊帳號的插件,原本 ownCloud/Nextcloud 只能管理員自己建立帳號然後把帳密交給新使用者。

開發其實很簡單,沒什麼技術難度,知道怎麼接 ownCloud public API (PHP 的 API)和基本的資料庫連接 code 的撰寫能力就可以了。已經很久沒有碰 code,有個 admin-approval 的 branch ,已經拖了兩年多了 :-/ ,要做的功能是管理員審核功能,讓管理員可以審核新註冊的使用者,也沒什麼技術難度,就是 glue code 接一接,所以實在沒什麼動力把它做完。前端部分會用 handlebar.js 去 hook 官方的使用者管理頁,然後去呼叫後端插件內的 HTTP API 。

facebook-not-typing 和 facebook-micromanage

這兩個都在我的 github 帳號底下,facebook-not-typing 應該看了 readme 就知道是幹嘛的。

facebook-micromanage 則是比較有野心的計劃,想要以瀏覽器插件來提供很多 Facebook 不給的功能,現階段已經完成撈朋友清單,接下來利用已經有的基礎建設,也就是撈 cookie 和 localStorage 的 code ,想要突破 Facebook 過濾泡泡,去撈每一個朋友和粉絲專頁的牆,然後存進 IndexedDB (雖然說這已經是 deprecate 的標準,但我真的需要結構化的資料儲存),再依照時間順序顯示出來。現在遇到的問題是撈朋友牆的 request ,分頁的參數不知道怎麼填,有幾個變動的神奇數字還沒有辦法看出產生的方式。

不過或許以後也可以直接開大絕用 phantomjs 撈更快,不過這要跑在瀏覽器插件裡面就比較有困難。不對,開一個隱藏的 content page 應該就可以?

第二個想做的功能是管理朋友清單,可以用交集和聯集產生新的清單,還有朋友清單備份時間點,也就是在每一個朋友清單後面加個時間戳記,最新的則不加戳記,在有變動(譬如說你把人加進那個清單)的時候,加入前的版本會加上戳記(清單重新命名),然後把原本的人加到新的清單,再加入那個新的人,這樣新的人就看不到你以前的貼文。

bsd-cloudinit

  • 弄好 Jenkins… 有夠難 debug 設定,應該接近完成了,等 Github Pull Request Plugin 的那個 bug (在 Firefox 按 Rebuild PR 沒反應)修好應該會比較好 debug,噢,還得再次砍掉 ghprb 。(每次要測試 build 還得去 github PR 下面留言 “retest" 實在有點蠢)
  • 跟上游 cloudbase-init 同步 code (因為他們是一個 framework 了,我們只是加幾個 FreeBSD 專屬的 class 來實作在 FreeBSD 上面的操作)
  • 同步 code 以後,把舊的 code 改寫成符合新架構的形式,然後開始收 PR ,也改成新架構
  • 弄個 cronjob 去檢查 FreeBSD releng 的更新,有更新的時候自動觸發 build

ownCloud Music Koel

從 Koel 幹來的前端,插在 ownCloud Music app 上面,還有很多功能沒有做完:

  • 歌詞,Last.fm,YouTube 整合壞掉了
  • 觸發掃描的按鈕
  • 樂曲長度偵測、顯示
  • 上架 apps.nextcloud.com

中文斷詞加貝式機率過濾垃圾郵件

附中信箱的垃圾郵件自從用了 bogofilter 之後算是蠻少的,不過有時候還是會有一整批的 Spam 抓不到。所以希望可以用結巴斷詞函式庫先把信內容斷一斷然後再進 bogofilter ,當然斷詞之前還有很多麻煩事要做,像是 parse MIME multipart ,幸好發現了個 library 可以解決。

現在是卡在要設計一個有效的 benchmark 方法,測量偵測的準確度。(跟其他專案一樣,就是好像怎麼做都可以,怎麼做都有點問題,沒辦法決定怎麼做,所以先擱置)

筆記會依照相關性記在 CNMC Wiki

要是中文垃圾郵件過濾可以做得好的話希望可以架台 mail server 給認識的人付成本價使用,也可以提供 domain email 代管,help re-decentralize the web

豐原計劃

是個自幹 Google Drive 的計劃,ownCloud 現在還是有很多問題,所以想要重新用 Django 實作,構想都記在 HackMD 裡面了。目前我在弄前端,雖然我跟前端真的沒那麼熟,但 @M157q 更不熟 :-p 所以只好我來弄。週末不定時黑客松。

skicka-fuse

拋棄很久的專案,想要用 skicka (非官方的 Google Drive CLI client,Go 寫的)和 bazil-fuse 重新做一套 Google Drive 的 FUSE, 希望用 Go 會讓一切變得很快,還有計劃要用大量的本地快取增加速度,但快取可以隨時被 free ,類似 ZFS 或 memcached 那樣。

逆向 iCloud Drive

希望可以找到方法 hack ,讓深度整合 Finder 的 iCloud Drive 可以被 hack 成其他 backend ,像是上述的豐原。

裝了 mitmproxy (在 OSX 上面用 pip 裝似乎會爛,放棄了直接裝上游 package),攔截了一些 request ,基本上 iCloud Drive 就是用 CloudKit 實作的,所以得先弄懂 CloudKit 的認證方式。

筆記也在 HackMD

Octowall

某次在 GitHub 亂逛看別人都在幹嘛的時候想到的主意:

//platform.twitter.com/widgets.js

Django 的初始 scaffolding 已經有了,但是埋藏在某個硬碟可能已經損毀的虛擬機器裡面,要繼續的話得設法撈出來。

桌面環境與豐原計劃(GNOME)

希望桌面環境和雲端環境能夠更深度整合,想修改 GNOME apps ,讓它們可以和豐原運作,譬如說 Nautilus 兼雲端檔案瀏覽器,類似 Dropbox Project Infinite 的概念。還有也想改改 Shotwell ,讓它可以做類似 Apple Photos 或 Google Photos 的事情,自動上傳照片,然後可以瀏覽遠端照片,就像在本地。

多虧 lantw ,已經在 Ubuntu 上面弄出 GNOME 的開發環境(這真的是最難的部分,讀懂 Nautilus 的 code 都比這個簡單,大概是因為我用 Ubuntu 的關係,然後官方在不久以前把 Ubuntu 從支援的開發環境中移除了,現在要開發的話,官方支持的就只有 Fedora。但是我不想重新學用 rpm 系的東西啊 :-p )。

第一步就是應該要可以 build 出一個跟 Nautilus 一模一樣但是可以和 Nautilus 獨立運作的應用程式,這樣才能方便開發和使用者測試,否則每次測試我們的東西 Nautilus 就有可能壞掉,多麻煩啊。可能主要是 DBUS 的註冊名稱要改一下之類的,但光是這個就挺麻煩,我跟 DBUS 不熟 :-p

目前擱置中,先做豐原,Web 畢竟還算是我稍微有能力做的事情,桌面環境的 build system 什麼的好複雜啊 @_@

BitTorrent tracker proxy

發現很多環境,像是學術網路,阻擋 BT 的方式多半是用阻擋特定的 port ,UDP tracker 會用的 port ,像是 6969, 1337 。然後 tracker 協定其實支援 TCP, HTTP, HTTPS ,但幾乎所有的知名 tracker 都用 UDP ,而且都用那幾個 port 。

所以想法是,做一個 tracker proxy ,可以在未受阻擋的網路,用 HTTPS tracker 協定和受阻擋的網路內的 client 溝通(計中總不可能 block HTTPS 吧),然後再代理 client 向上游的 tracker 用 UDP 溝通。

這樣有幾個問題,一是計中還是可以擋我們 proxy 的 IP ,但這應該不會那麼快發生,而且我們只是跑普通的 HTTPS 啊,為什麼要擋我們捏?完全杜絕的方法就是用 public cloud, 讓他們一擋就要擋所有服務,不過 Heroku app 沒辦法送 UDP 的樣子,得用其他共用 IP ,又可以送 UDP 的 public cloud。問題二是,這樣使用者每次要新增 torrent 的時候就得手動新增我們的 proxy 到 tracker 清單裡面,很麻煩。這應該可以用在 client 做插件的方式解決,Deluge 是 Python 寫的,也可以用 Python 寫插件,應該不會太難。問題三是代理沒辦法知道要幫使用者代理哪幾個 tracker ,這個解法一是用自訂的協定去溝通,client 端用插件送額外的資訊;解法二是我們就只是 proxy 幾大 tracker ,其他就不管,這樣應該至少可以達到一點點效果吧(其實不算是解法啦…),還可以開個網頁允許使用者自己加入想要 proxy 的上游 tracker 。

本專案還沒有任何 code ,不過我在 trace bittorrent-tracker 的 code 了。

徵人啊

以上專案全部都徵人和我一起做,沒有很懂沒關係,我可以教,不然這些構想真的都沒辦法實現了。

因為找不到人一起寫 code ,所以辦了臺灣程式路跑

臺灣程式路跑是我和子期舉辦的活動(還要靠很多朋友和導師的幫忙啦),希望傳承台灣現有自由開源軟體開發者的經驗給新進者,讓更多人接觸自由開源軟體開發。這篇文章說明一下我為什麼想要辦這個活動背後的脈絡。

——

最一開始其實是我去柏林參加兩次 ownCloud 黑客松回來之後產生的想法。感覺,對於這些歐洲開發者(裡面大概一半是歐洲其他地方來的)來說,寫開源專案好像只是「空閒時間跟朋友進行的休閒活動」而已,可能跟我們會約朋友一起打球很像。就算一開始不認識,也會透過 IRC 上面的閒聊(通常不會在 GitHub 或是 mailing list 上面閒聊)互相認識,而且大概又因為是屬於相近的文化圈和時區,本來的同質性就高,所以到最後多半可以互相認識。——感覺有點像在臺灣參加社群研討會籌辦一樣,大家一開始不認識,但是在為了同個目標工作的過程熟悉了彼此。

「貢獻自由開源軟體在歐洲是一件稀鬆平常的事情」,也跟風氣脫不了關係。很多人都在做,所以不覺得有什麼稀奇。在臺灣除了一開始貢獻的人數就比較少,二方面是,貢獻者之間的連結似乎沒有像在歐洲那麼強,很容易只知道自己的專案,不知道有其他人在寫、在寫些什麼,所以感覺上自由軟體貢獻者更少了。而自由軟體貢獻者在臺灣的稀少性,更強化了「貢獻自由軟體的人都很厲害」、「要很厲害的人才能貢獻自由軟體」的想法,無形中產生了心理上的門檻。

——

我也是那種新奇想法很多,但是很少堅持到底完成一個專案的人。我的興趣變的太快,幾乎每一天都在變,今天想 hack 某個專案,明天想讀另一個專案的程式碼,後天想研究什麼東西的架構。更別說每個禮拜還會突然發現某個功能很重要,為什麼沒有,甚至在洗澡的時候冒出超酷炫新專案的構想。構想的實現速度永遠比不上增加的速度還有在不同計劃之間 context switch 的時間。

計劃這麼多,卻因為自己似乎對什麼都有興趣,什麼都想學,反而沒什麼實際產出。這個問題苦惱了我很久,直到鳳梨酥計劃開始之後我才發現解法就是找人一起來。

鳳梨酥計劃的開始,還有高中的時候和同學整個暑假開發學校網站的經驗很類似。鳳梨酥計劃一開始只不過是因為我們都覺得在 OpenStack 上面跑 FreeBSD 很麻煩,於是隨口在社辦約了黑客松,另一個人看到我們的黑客松也說要加入,就形成了這樣的三人團隊。裡面正好我搞 OpenStack ,負責手動測試我們的 code ,Iblis 寫骯髒的 shell command ,Apua 寫 Python 。我們只花了大概三四次黑客松就把最基本的功能完成。每一次黑客松都很開心,大家遇到哪裡卡住馬上可以問另一個人,馬上測試也可以讓寫 code 的人有馬上的回饋,然後最後大家再說說笑笑去吃宵夜。高中的時候則是,我記得整個暑假我們不是泡在社辦(還用櫃子在角落隔出一個空間,裡面鋪上學校新採購電腦主機的大片包裝用塑膠軟墊,讓我躲在裡面睡午覺),就是整天掛在 Skype 上面 cowork 。

有朋友一起寫程式,不只是比較好玩、有人可以討論,對於我這樣三分鐘熱度每天轉移焦點的人,為了不和朋友失約,可以約束自己專注在一個專案比較久而不會分心去玩其他東西。

所以從這兩次的經驗,我瞭解到要做長久的專案,一定要找到志同道合的朋友。

——

既然如此,下一步很簡單,我就想辦法找人一起寫我覺得很有趣的專案就好了,一開始我一廂情願地覺得我只要跟他們解釋這個專案的願景,還有我發現他們現在沒有的功能、可以從哪裡參考類似的實作、有了這個功能之後世界是多麼美好,就可以說服他們一起來跟我寫 code 。——顯然我錯了,出乎當時的我的意料之外,我講的幾乎每一個構想,朋友們聽了之後的回應大多都是「哦,我覺得還好啊」。

這時候我才意識到,即使同為程式設計師,人之間的興趣還是有如此鴻溝般的差異。

也差不多是同個時期,我正在為自己維護的開源專案感到心煩,一個承諾已久的重大功能。雖說是重大功能,實在提不起動力實作,不是因為難度太高,而是因為完全沒有技術創新性,總覺得不過就是 API 接一接這樣簡單的事情。這樣一拖就拖了兩年了, ownCloud 都裂成兩半變成 Nextcloud 了。

我想到,有這麼多人想要踏入開源軟體的開發,也有許多開發者因為必須要開發那些不算是重複但是技術不有趣的必要功能感到厭煩(像我一樣),這兩者是不是可以結合,讓這些原本的維護者懶得寫的 code 作為新進開發者熟悉專案的練習?

這就是程式路跑活動的初步想像。

——

我和子期也開始進行很多討論,尤其是:我們到底要找哪一些專案的導師來?

一開始的想法很模糊,大概是「不要找那些商業型的開源專案」,有很多公司將專案開源只是為了行銷並且獲得免費的開發人力,舉個最極端的例子就是 MIUI ,早期很熱門的 Android 第三方韌體,一開始是開源的,因為漂亮的界面吸引了很多人,開發者免費幫他們把 ROM 移植到不同裝置上面,使用者則是開開心心地體驗了小米公司的軟體,順便幫忙推廣給親友。後來知名度高了,小米公司就拋棄開源的 MIUI 維護,不再釋出新版 MIUI 的源碼。高明的行銷。(順帶一提,我感覺以後 Deepin Linux 也會這樣,中國沒有什麼軟體公司真的有社群精神或是社會責任的,一切以賺錢為優先,大概是競爭激烈的文化氛圍所造就的吧)

另一種商業型的開源專案則像是 OpenStack ,OpenStack 能夠維持開源同時維持熱門,主要的原因就只是對於這些參與 OpenStack 開發的公司來說,開源是能夠創造最大利益的選項,雖然無法排除競爭者從原始碼得利,但大家一起合作寫 code ,這些比較小的公司也才有機會和大型競爭對手 VMWare 甚至是 AWS 競爭,他們可以基於這個共同的開源平臺開發專屬的、不開源的武器,因為只要是相容於 OpenStack 的就可以使用,拉近了這些小公司和大型競爭對手的距離,然後再用專屬的武器突圍。

OpenStack 這樣的專案絕對堪稱是開源軟體的典範——但我們還是不希望程式路跑納入 OpenStack ,原因是一般的個人使用者幾乎不可能會直接用到 OpenStack ,而既然程式路跑以純粹個人的背景出發,我們考量的也是廣大個人用戶的利益,引用自活動的 FAQ:

有許多 FOSS 軟體背後有企業支持,如 OpenStack, Tensorflow ,有企業支援,這些專案要找到貢獻者相對容易,相對來說有許多得不到企業支持的 FOSS 軟體,只能靠現有貢獻者的熱情,甚至捐獻金錢才得以繼續,身為一個保持自由開源理念、駭客精神的個人,我們(主辦人)想要將我們的一己之力集中在沒有企業支持的 FOSS 軟體。

這樣的選擇,也是出自於一個疑問:臺灣這麼多 Linux/FreeBSD 桌面環境的使用者,其中有不少具有相當的程式能力,為什麼一些重要的桌面環境開源專案,像是 GNOME, KDE, ibus-chewing, Firefox 的臺灣開發者還是這麼少?(甚至是 bug report 來自臺灣的都很少?)

選擇開源桌面環境的使用者,我想或多或少都抱持一點理想性,認為自由軟體才能帶來使用資訊科技時真正的自由。而這點理想性,是我們以學生、年輕人,還不必負擔太多社會現實的身份負擔得起的,所以我們花時間精力舉辦的程式路跑,希望集中力量在這樣的自由軟體上面。

——

這個活動非常高興能夠獲得各方支持,尤其是幾位導師,Tim, Weicheng, 呂行, Mark, Acelan, Franklin, 4$ ,幾乎什麼都沒有問的馬上就答應支持,中華民國自由軟體協會慷慨的經費贊助,還有師大資訊社阿哲幫我們弄到場地。

我覺得活動算是蠻順利,結果也很成功,大家都有作出貢獻,幾個 bug report, patch 。也真的有產生出我想要的那個氛圍——坐在教室,真的有那麼一刻,我以為我回到 ownCloud 黑客松那時候柏林工業大學的教室裡面,大家吃東西討論 code ,很歡樂的黑客松的感覺。

希望真的有幫到臺灣的自由軟體和開發者就是。

(跳過八月的文章沒寫這次居然寫了三千多字,就當作是補償吧,發了之後 WordPress 告訴我這是這個部落格第兩百篇文章啦,真是值得慶祝)

Fix building github.com/popcorn-official/popcorn-desktop

If you try to build from development branch, the result is not usable. I’ll explain the hacks you need to make it work.

Problem 1: 403 on nw:build

As reported on github: https://github.com/popcorn-official/popcorn-desktop/issues/202

Background on nw-builder

Basically what nw-builder does is that it fetches the prebuilt binaries of nw.js and copy the source code of your app and bundles it into a package. It is also possible to directly run your app without bundling it using the method described in the nw.js documentation:

cd /path/to/your/app
/path/to/nw .

According to this comment:

Just for you know that we have an strong dependency on NW, which is compiled PCT team.
PCT NW are compiled with anothers video codecs (something like that).
Maybe you should know this.

Note: I copy the entire contents from links in case of DMCA takedowns

Since the PCT binary URL is returns 403 now (not sure if it’s due to DMCA takedown), I thought about using the vanilla binaries instead, but this way we would lose a few codecs. Then I figured I can extract the binaries from the working packaged version 0.3.9 (it’s on the front page).

Download it, unzip it, copy Popcorn-Time.app to popcorn-desktop/cache/0.12.2/osx64/nwjs.app, and rm -r popcorn-desktop/cache/0.12.2/osx64/nwjs.app/Resources/app.nw otherwise nwjs will simply look for source code inside app.nw to run instead of your repo source code at popcorn-desktop.

Now we can start popcorntime from your source code with:

cd popcorntime-desktop
cache/0.12.2/osx64/nwjs.app/Contents/MacOS/nwjs –development .

Note: I read somewhere of the --development flag, but don’t observe any change in behavior, I’ll just leave it there.

Soon you’ll hit error like this (debug log at popcorn-desktop/cache/0.12.2/osx64/nwjs.app/Contents/MacOS/debug.log):

[43854:0710/142626:INFO:CONSOLE(27)] ""added" "TVShowTime" "to provider registry"", source: app://host/src/app/lib/providers/generic.js (27)
[43854:0710/142626:INFO:CONSOLE(35)] ""loaded" "tvshowtime.js"", source: app://host/src/app/bootstrap.js (35)
[43854:0710/142626:INFO:CONSOLE(27)] ""added" "Watchlist" "to provider registry"", source: app://host/src/app/lib/providers/generic.js (27)
[43854:0710/142626:INFO:CONSOLE(35)] ""loaded" "watchlist.js"", source: app://host/src/app/bootstrap.js (35)
[43854:0710/142626:INFO:CONSOLE(27)] ""added" "ysubs" "to provider registry"", source: app://host/src/app/lib/providers/generic.js (27)
[43854:0710/142626:INFO:CONSOLE(35)] ""loaded" "ysubs.js"", source: app://host/src/app/bootstrap.js (35)
[43854:0710/142626:INFO:CONSOLE(21)] ""%c[%cERROR%c] Error starting up" "color: black;" "color: red;" "color: black;"", source: app://host/src/app/app.js (21)
[43854:0710/142626:INFO:CONSOLE(16)] ""[%cWARNING%c] Show Disclaimer" "color: orange;" "color: black;"", source: app://host/src/app/app.js (16)

Problem 2: Error starting up

With my limited Javascript knowledge, I added the lines to print out debug messages at app/database.js#447:

446             .catch(function (err) {
447                 console.log(err.message);
448                 console.log(err.stack);
449                 win.error('Error starting up', err);

Got the error:

Error: Cannot find module 'butter-provider-MovieApi'
at Function.Module._resolveFilename (module.js:327:15)
at Function.Module._load (module.js:264:25)
at Module.require (module.js:356:17)
at require (module.js:375:17)
at window.require (\u003Canonymous\u003E:4:17)
at Object.getProvider [as get] (app://host/src/app/lib/providers/generic.js:54:60)
at app://host/src/app/lib/config.js:227:42
at Function._.map._.collect (app://host/src/app/vendor/underscore/underscore.js:172:24)
at Object.Config.getProviderForType (app://host/src/app/lib/config.js:226:26)
at app://host/src/app/bootstrap.js:85:42
at Array.map (native)
at app://host/src/app/bootstrap.js:83:56
at Promise_then_fulfilled (/Users/pellaeon/project/popcorn-desktop/node_modules/q/q.js:766:44)
at Promise_done_fulfilled (/Users/pellaeon/project/popcorn-desktop/node_modules/q/q.js:835:31)
at Fulfilled_dispatch [as dispatch] (/Users/pellaeon/project/popcorn-desktop/node_modules/q/q.js:1229:9)
at Pending_become_eachMessage_task (/Users/pellaeon/project/popcorn-desktop/node_modules/q/q.js:1369:30)"", source: app://host/src/app/database.js (448)

grepping butter-provider-MovieApi in the source tree returns no result, neither does googling.

Then I tried copying the entire working source tree from the packaged version Popcorn-Time.app/Contents/Resources/app.nw/, this doesn’t make the error go away either, which indicates that the problem lies not in the source tree. But where could the problem be?

The only one I could think of was the 2 files you need to download separately from popcorntime.sh to build it: popcorntime.sh/torrent_collection.js and popcorntime.sh/ysubs.js (instructions in readme). Anyway this doesn’t work either.

I came across issue#102:

The popcorntime.sh/ysubs.js downloaded is invalid, after pct load the file, it is not added to provider registry. I changed ysubs.js minified content by ysubs.js from branch feature/electron (old version of the file) and it works.

Hmmm, very suspicious.

But diffing ysubs.js doesn’t seem the downloaded version have anything different that would make the error go away, nor does actually installing it, nor is torrent_collection.js, which lies inside the view directory, even less likely.

So looks like I need to dig in to the trace to see what I have missed.

Digging into the code

With the trace above, I decided to go with bootstrap.js first, between line 83 and 85, let’s print out the variable first:

83             return _.filter(_.keys(Settings.providers).map(function (type) {
84                 console.log('========1');
85                 console.log(type);
86                 return {
87                     provider: App.Config.getProviderForType(type),
88                     type: type
89                 };

Running again, the log gives:

[46099:0710/164418:INFO:CONSOLE(84)] ""========1"", source: app://host/src/app/bootstrap.js (84)
[46099:0710/164418:INFO:CONSOLE(85)] ""movie"", source: app://host/src/app/bootstrap.js (85)

Hmmm, nothing too special, don’t look deeper just now.

Let’s see what it later calls: getProviderForType(). See the vars again:
config.js

225             } else if (provider instanceof Array || typeof provider === 'object') {
226                 return _.map(provider, function (t) {
227                     console.log('==========2');
228                     console.log(t);
229                     return App.Providers.get(t);
230                 });
231             } else {
232                 return App.Providers.get(provider);
233             }

Log:

[46099:0710/164418:INFO:CONSOLE(227)] ""==========2"", source: app://host/src/app/lib/config.js (227)
[46099:0710/164418:INFO:CONSOLE(228)] ""MovieApi?&apiURL=https://movies-v2.api-fetch.website/,"", source: app://host/src/app/lib/config.js (228)

…wait, isn’t that the non-existent module’s name? “butter-provider-MovieApi“, and after that a URL that doesn’t look valid at all: movies-v2.api-fetch.website, verryyy suspicious (Note: I later found out it is actually valid URL). grep it:

popcorn-desktop$ grep -R 'MovieApi' *
node_modules/butter-settings-popcorntime.io/index.js:             uri: ['MovieApi?'

What ?!! node_modules ! It gets settings from node_modules ?!

grep in packaged version returned no result. So this is what differs between 2 versions!

I copied index.js from packaged version to popcorn-desktop/node_modules..., and this time it started successfully, and is able to load all movies and TV shows!

What really happened

Looking at package.json from the 2 different butter-settings-popcorn.io directory, seems like the two do differ in versions, master uses c745869979405d02d5a4cfea066c227801ee0fc2 (of butter-settings-popcorn.io), 0.3.9 uses 6b8075e0065efbe7276e4a469ca00dbc43c2aaa9. diff:

git diff 6b8075e0065efbe7276e4a469ca00dbc43c2aaa9..HEAD
diff --git a/index.js b/index.js
index dce0e24..ccfcec1 100644
--- a/index.js
+++ b/index.js
@@ -16,10 +16,10 @@ module.exports = {
movie: {
order: 1,
name: 'Movies',
-             uri: ['yts?'
+             uri: ['MovieApi?'
+'&apiURL='
-                     + 'https://movies.api-fetch.website/,'
-//                     + 'cloudflare+https://xor.image.yt,'
+                     + 'https://movies-v2.api-fetch.website/,'
+//                     + 'cloudflare+https://movies-v2.api-fetch.website,'
//                     + 'cloudflare+http://xor.image.yt'
]
},
@@ -28,8 +28,8 @@ module.exports = {
name: 'Series',
uri: ['TVApi?'
+'&apiURL='
-                     + 'https://tv.api-fetch.website/,'
-//                     + 'https://odgoglfi7uddahby.onion.to/,'
+                     + 'https://tv-v2.api-fetch.website/,'
+//                     + 'cloudflare+https://tv-v2.api-fetch.website,'
//                     + 'http://tv.ytspt.re/'
]
},

Testing the URLs in it I found it is actually valid (must be the new gTLDs).

Seems like the name before the question mark is the key, there is butter-provider-yts but no butter-provider-MovieApi in node_modules, so it couldn’t load the latter and errored.

Butter will automatically load any npm package installed (listed in package.json) that matches the =/butter-provider-.*/ regex.

I guess this is in startup stage, and in later stage someone would request the MovieApi or yts provider, but I still haven’t found the code.

Anyway, this fixes the build, so another time!

90 行的開源貢獻

https://github.com/pellaeon/fb-post-bulk-privacy-edit

去年我花了幾個晚上用僅有的 Python 技能寫了一隻很小的只有 90 行的 script ,可以幫你大量編輯一段時間內的 Facebook 貼文隱私設定。

今天早上收到信,感謝我寫了這個,然後問了一個問題。

回去看那個 repo ,居然也有另一個人回報了 issue 。
我覺得超驚訝,這樣一個小小的 90 行的程式也能造成這麼大的影響啊。

其實開源貢獻真的沒有很難。

ownCloud Contributor Conference Berlin 2015

grouppic2015

今年8月,我再度參加在柏林舉辦的 ownCloud Contributor Conference ,雖然名為 Conference ,但是活動大部分的時間是黑客松。

ownCloud 是一個線上檔案儲存分享的軟體,用一句話講就是 open source 的 Dropbox , 不過最近除了檔案儲存和分享,也很著重在檔案周圍的應用,像是音樂播放器、圖片管理。

我開始參與 ownCloud 是2年前,我想在學校自己架 ownCloud 開始的,當時大概還是 ownCloud 6 吧,問題很多,所以我就自己修掉問題,發了人生中第一個 issue report 和 pull request ,有了第一個,後來就有了第二、三、四個。

活動前一天,我去幫忙主辦人搬活動期間的飲料和各種物資,有各種飲料、餅乾、起司、火腿、麵包,還借了一個冰箱!

我們的活動場地是柏林工業大學(以下簡稱 TUB)數學館,是個熟悉的地方,活動在這邊辦好幾年了。聽主辦說 TUB 每一年都讓我們免費用場地,有好幾間教室、2間大階梯教室,而且每間教室還幫我們架好有線網路、電源,原因是 TUB 自己有一個非常大的 ownCloud 安裝,全校的學生和教授都在用。

活動第一天是早上是 Angela Richter 的 Keynote ,主題是 “How artists can help create awareness of technological dangers” ,講者是一位舞臺劇編劇。她在製作《伊底帕斯國王》的期間發現 Scientology ,並為此很感興趣,閱讀了很多資料,而其中很多資料都是 WikiLeaks 洩漏出來的。後來她又進而對 WikiLeaks 產生興趣,想製作一齣以 WikiLeaks 為主題的舞臺劇,她很幸運地在 Ebay 買到跟 Julian Assange (WikiLeaks 創辦人)共進午餐的機會,Assange 對這個舞臺劇也很感興趣,跟她對談了好幾次。她開始參加各種聚會,像是 HOPE (Hackers On Planet Earth) 和 CCC (Chaos Communication Congress) ,認識了很多駭客。

接觸 WikiLeaks 之後,她發現更多關於吹哨者 (whistleblower) 的事情,史諾登事件爆發之後,她決定製作另一齣以吹哨者為主題的舞臺劇,希望藉此提倡資訊安全、政府監控、言論自由等議題。這一齣舞臺劇就是這次演講的重點。

她的演講引起臺下很多人的共鳴,因為,這正是德國資訊圈(駭客圈)最重視的事情——隱私權與言論自由。

從一般人的觀點來看,可能會覺得很奇怪,Google Drive 那麼好用,又不用錢,幹嘛還要自己花時間架一台雲端檔案儲存?甚至是台灣的資訊圈,對於隱私權和言論自由的重視程度也不及歐美。

我曾經被一個台灣人問過:「如果每個人都有高度隱私,那警察要怎麼抓罪犯?」

在台灣,「我沒犯法,沒什麼好藏的,要看就給他看,沒什麼好怕的」的想法也很常見,這是對於網路監控最常見的反應,還有一個維基百科頁面專門介紹。眾多資安界人士對此作出反駁,Bruce Schneier, Moxie Marlinspike, Glenn Greenwald

註:我還在 Schneier 的部落格找到一個論文的連結 “’I’ve Got Nothing to Hide’ and Other Misunderstandings of Privacy”

我整理節錄 Schneier 和 Marlinspike 的論點如下:

Marlinspike 說,想像一個法律百分之百會被執行的世界,所有的罪犯都會被找到,所以也就沒有人會進行違法的舉動,可是我們的社會其實就是不斷地打破規則,如果沒有那些違反規則的人,我們也就不會發現規則本身的問題。在這個想像的世界裡面,沒有人會進行同性戀行為或是使用大麻,可是如果沒有人會進行同性戀行為或是使用大麻,我們怎麼知道為什麼要禁止同性戀、使用大麻?沒有這些人,我們就不會發現法律的不合理處,社會就不會進步。

過去,執法比較困難,要調查一個人必須實際派一個探員去跟蹤他,因此大規模監控不可能。現今,每個人手上的智慧型手機就是追蹤器,而大規模監控也早就是進行式了,在加上各種自動化程式,找到每個人的把柄變得輕而易舉,而且幾乎每個人都有,如此複雜的法條讓你根本就不知道什麼是罪,什麼不是,舉例來說,你知道在美國持有一定大小以下的龍蝦是聯邦罪行嗎?到這個地步,要處罰誰已經變成完全選擇性的問題,看誰不爽就翻他的底細找個理由定罪。

Schneier 則是從另一個層次來說:隱私是人類基本的尊嚴。很多人以為隱私的重要性是在於隱藏「錯誤」的事情,可能是違法的、或是不被社會所喜歡的,但不是,隱私是基本人權。隱私可以防止當權者濫用權利,俗話說得好,「誰來監控監控者?」、「絕對的權利帶來絕對的腐敗」。很多人誤以為隱私議題是「治安對抗隱私」,但其實應該是「自由對抗獨裁」。

拉回來說,台灣受於中華文化的影響,似乎比起歐美人更相信政府,相信政府會維持治安、照顧經濟(父母官的概念),而不是限制他們的自由。而我覺得事實上台灣政府的作為也比一些歐美國家溫和收斂許多,至少不會像美國發生警察濫權,或是政府千方百計想要竊聽人民的事情,所以台灣人大多數不會覺得政府在控制他們,也就對隱私不那麼重視。(直接講就是,我不保護也不會有人來侵犯,所以保護的意識就沒有那麼強烈了吧)

而在這個強烈的 “Own your data” 的意識下,ownCloud 這類的軟體會如此熱門也就可以理解了。

——

ownCloud Contributor Conference 活動主要分成2個部分,週六和週日有演講及 workshop ,而週一到週四則是黑客松。

演講的部分,除了 Angela Richter ,週日早上的講者是 Holger Angenent ,介紹 sciebo ,是一個基於 ownCloud 的大型雲端儲存服務,提供給德國的大學及研究機構。不過我其實沒有聽到這場,當時我在演講廳外的報到區幫忙顧攤位,一邊跟另一位希臘貢獻者聊天。

我參加的幾個 workshop 主要是在介紹如何貢獻 ownCloud 、ownCloud 的架構,還有幾個資訊安全、UI/UX 設計的 workshop 。

我在 ownCloud 的貢獻一開始是以修核心的 bug 為主,後來我原本想幫他們加使用者註冊功能,我做完之後送 PR ,他們才說希望實作成一個 app (在 ownCloud 術語裡面,app 其實比較接近 plugin 的意思),所以我才慢慢開始研究怎麼寫 ownCloud app ,今年 5 月的時候,才釋出我的 Registration app

去年寒假,我拉了一個熟悉 Angularjs 的朋友,跟我一起來改 Music app ,當時的 Music app 使用 SoundManager2 ,使用 Flash player 來播放 MP3 ,但是因為我的音樂大部分都是 FLAC 格式,沒辦法用 Music app 播放,因此決定想要利用 Aurora.js ,來達成可以在瀏覽器裡面直接播放 FLAC 及其他格式的音樂。最後因為 chunking 問題卡住,Music app 從 ownCloud 抓檔案的 URL 不支援 chunking ,所以 Aurora.js 必須把檔案完整下載完才能開始解碼播放,我們覺得這樣實用度大大降低,因此放棄了完成的 code 。

直到去年我參加 ownCloud conference ,跟 Music app 的主開發者 Morris 聊過之後,得到他的幫助將檔案的 URL endpoint 改成 ownCloud 提供的 WebDAV endpoint ,這是支援 chunking 的 endpoint ,並且在他的幫助下把寒假完成的 code 正式 merge 進 Music app 。

5天的黑客松,在 ownCloud 的 Security Officer (?) Lukas 的 workshop 上,我問了他一些 ownCloud public API (給 app 使用的 API )安全性方面的問題,譬如說怎麼啟用內建的 CSRF 保護,他也稍微幫我 review 過我的 app 。

還有像是修正 app 的 routes 設定正確地使用 ownCloud app framework 等等,中間也聽到很多 ownCloud 開發的內幕,譬如說為什麼會有某些骯髒的低效能設計、未來打算怎麼改之類的。

Music app 的部分,去年把 SoundManager2 換成 Aurora.js 之後,OGG 格式的播放就一直是壞的,因為 ogg.js (Aurora.js 後端的 Javascript decoder )是壞的,我很久之前就回報給作者但是他沒打算修的樣子。今年我為了要重新支援 OGG ,決定再把 SoundManager2 拉進來,可是又要維持 FLAC 可以播,所以我做了一個 wrapper ,把 SoundManager2 和 Aurora.js 包起來,依照檔案格式決定要用哪一個。其實之前從來就沒有認真學過 Javascript ,基本上是用對高階語言的基本認知在寫的,還好 ownCloud 團隊裡面也有 Javascript 大師 Vincent ,回答了我很多問題。

Morris 也跟我講了 Music app 未來的計劃,要從 Angularjs 轉移到 Backbone.js ,原因是太少人會用 Angular (我自己就不會用), 太難學,太難 debug ,提高了貢獻者的門檻。我有點訝異對於他們來說,貢獻者門檻是如此重要的考量,願意為了降低門檻而重構。

說到這個,藉由這次的活動,我也得以一窺大型 FOSS 專案的運作,和他們的態度。除了上面那件事之外,有次聊天時,Vincent 也提到,ownCloud 的開發者們都很分散,München, Stuttgart, Berlin, Nürnberg 都有一些人,但他們除了一年一度的 contributor conference,就沒有其他聚會了(線上會議也沒有),他們很重視這個社群的透明性,所有的討論都應該公開進行並且被記錄,寧願犧牲溝通的方便性也要保持公開透明,好讓任何人都可以參與。

——

在黑客松期間非常的快樂,讓我想起高中暑假那段每天從早到晚都跟夥伴一起寫 code 的日子。上大學之後,技術能力提升了,人脈廣了,見識也多了,經常遇到有趣的 project 、各種活動、各種靈感,事情越接越多。事情都很有趣,但事情一多,就必須不斷的 context switch ,那種單純地寫 code 學習,專注在同一個計劃的日子卻少了。在國外參加黑客松,正好給自己一個理由拋下手邊所有的計劃,只專注在 ownCloud ,一旁的夥伴大家一起致力於同一個計劃,互相幫忙的那種感覺,好久不曾有過了。

這正也提醒了自己,回去之後,每隔一段時間要出去獨自旅行,或是閉關幾天專注在同一個計劃上,才能不忘接觸程式最初的感覺吧。

可怕的碩論英文

昨天看到一堆可怕的碩論英文文法之後就開始好奇碩論的英文水平到底有多高……

原本想用 Scrapy (crawler) ,再找一個文法檢查軟體之類的來自動化為所有論文評分,然後依據各系、各教授來統計,結果一定很有趣。

可是我找了很久找不到夠厲害的分析軟體,很多連基本的錯誤都抓不出來。

Gingerice 是 Ginger software 的 wrapper ,像是 “I go America" 他可以正確抓到應該是 “I go to America",但是接近的 “I go New York" 就抓不到錯誤。

LanguageTool 是開放的 proofreading 軟體,被 OpenOffice 使用,它連 “I go America" 都抓不到錯誤……

所以原先這計劃就只能等我找到夠厲害的 proofreading 軟體或是自己寫一套出來再說了……

我看到的英文究竟有多糟呢?這邊是幾個我找到英文特別糟的碩論(我只看資工系的,而且看不到全文所以只看英文摘要)