2020年11月20日 星期五

[動漫] 出租女友 / 租借女友 / 彼女、お借りします / 女朋友,借我一下

出租女友

夏天研究 bilibili 時,逛了一下排行榜而認識了這齣動畫,接著就在 巴哈姆特動畫瘋 二倍速看完了第一季動畫,然後跑去看了線上漫畫追到 165 話(每週二出刊),也趁雙11 Bookwalker 特價時,買了七本單行本紀念一下。

wiki 簡介,作者發想的點子是來自於中國春節返鄉時,網路上在那邊哈拉租個男朋友/女朋友回家過年的話題,當然,動畫加了很多青少年幻想色色元素 XD 但撐過後這片段後,後面反而可以享受劇情,著重男主角在認識自己,認識自己的心意究竟為何。我看完第一季動畫後,第一個想起的是很久以前的動漫 古靈精怪/橙路 (作者2020年10月病逝),就主角與阿圓和阿光的故事。最近看新聞說第二季動畫也決定開拍了,之後有空再來看吧!

老實說,年紀大了強迫自己看些年輕的東西也是一種醒腦的方式,像是回想起自己,看到一些有才華的人,想要多接近多了解、攀談時,最後發現彼此距離非常遙遠,才驚醒先天上本來就是不同空間的人事物,強求不來,還是回歸充實自己才是最重要的事,一切就是緣分(無緣)啊。

此外,看這動漫最大的收穫是認識日本樂團 the peggies !! 心煩的時候、無聊的時候聽一下 センチメートル 回想起歌詞意境心情也輕快許多(影片可開字幕看翻譯)


有興趣可以瀏覽 Spotify 別人整理好的 playlist:

2020年11月18日 星期三

使用 Bookwalker 電子書漫畫閱讀經驗

鬼滅之刃文宣

可能之前接觸太多鬼滅之刃的東西,被一堆廣告亂襲擊中,也順勢認識了一間銷售電子書的網站:BookWalker 台灣漫讀,而雙11有6折優惠活動,且新用戶第一次購買又可以享受79折,就這樣定價一本 55元的電子書漫畫,就只要25元左右,實在便宜,就買了 19 集(推論還有2集沒上架,在東立出版社的線上書城 11/16 才剛上鬼滅之刃第20集電子書漫畫

就這樣,雙11我只買了電子書漫畫 XD 幾百元而已。

這次買了19本,再看他一次 XD 第一次觀看時,覺得漫畫畫得很急似的,每個出場都很快,殊不知第二次來看,反而覺得都恰到好處,大概就知道全部才21集吧,這時就覺得人設規劃得很不錯,人物故事講得剛剛好,又是另一種體會。但說真的,我覺得動畫是讓這部漫畫封神的主要原因,那個 ufotable 製作的太出神入化 XD 而研究後其實很多漫畫也都是這樣,都透過動畫才達成另一個知名度高峰。

提一下 BookWalker 的情況,其實台灣也有很多電子書商店,他們不一定會做實體閱讀器,但至少都會提供 app ,以此達到購買後有地方可以閱讀。這次我也稍微研究了一下 BookWalker ,是一間高層都是日本人經營的公司:

大概看了一下發展,就覺得是還滿專注動漫畫的公司,其中粉絲團可以看到一直有在推出漫畫上架,所以就體驗一下囉!唯一感到不便的是一次買整套漫畫,要在 PC 或 App 瀏覽時,少了以漫畫為單位的顯示列表,目前都是以單行本為單位,若買了兩三部漫畫時,一頁只顯示 15 本的書籍單位時,就會被洗版 XDD

目前最常用 PC 閱讀,其 PC 閱讀環境是純瀏覽器,直接在網站看。至於 ios/android 都有對應的 app ,可能只有 iPad 是比較好的閱讀環境,畢竟尺寸還是很重要的 XD

[韓劇] 返回18歲 / 18 again / 18 어게인

圖片來源:wiki - 重回18歲

前陣子登入愛奇藝回顧一下方案(雙11方案),就被這韓劇吸引,當然,就是被女主角 金荷娜 吸引啦,看了幾齣她演的韓劇,印象深刻,像是某些情境會使出招牌的咬唇 (誤) 不過在返回18歲裡頭很少,我只有印象中看到一次。

後來發現以前公司的女同事也在看,當然,看法就不一樣,就是看帥哥男主角 XD 我一開始有點難適應那誇張燦爛的笑容 XDD

回過頭來,這齣戲是著重在親情戲的,剛好很適合我的年紀,提醒自已對小孩要多包容、多把握一起相處的時機。對年過35的來說,看的很有滋味,且愛情戲也不重,恰到好處,十分完美。

後來還在 NETFLIX 觀看了美國 2009 年的熱門電影 17 again (回到17歲),是返回18歲改編的來源。最後也研究了一下這部戲劇是不是很紅,好像是普通,平均收視 2.7%,因為同時段有 Penthouse 收視率可以衝破 10% ,也有青春紀錄等隨便也都 4% 收視率的。可能 TA 群不同吧,恰好給中年人回顧一下對家庭的時間投資。

2020年11月17日 星期二

[書] 彼得原理

幾個月前趁著 kobo 特價活動時,買了數本電子書,其中一本就是彼得原理。

這本書一開始閱讀時,對我而言像是八卦書,把自己已經在職場上看過的人事物統整起來,比較像複習,大概看完七成時都還是差不多的心得。

舉例來說,以前加入過五千人規模的公司,裡頭的文化氣息步調緩慢,再加上非營利的經營路線,考績跟年終影響沒那麼明顯,例如很努力可以得到 2個月年終獎金,而過的混混的可以拿到 1.5 年終,再加上彼此的薪資水準都是法定,範圍差距很小。

通常都是年紀大的比較懂文化,再加上年資高的月薪自然又被新鮮人高,這樣算年終下來,懂生活的老前輩不見得會輸給衝勁十足的新鮮人,就像彼得原理提到的人物一樣 XD 下一刻呢?有衝勁的年輕人習得文化後,要嘛一起享受生活,要嘛就離開此處。算是個劣幣驅除良幣的現象。

再來一個案例,我也曾碰過握有權力的前輩,本身也有才華,卻因為做事有自己偏好,很多苦勞都會刻意避開,實在聰明。有時都會覺得真是可惜,若那些苦勞都扛下來後,肯定會長出美好的果實的。再說一個案例,那就是老闆愛將模式下,會替愛將規劃一些政治圈,包括產生一些並非適合公司現狀的階級制度等等,這個叫做很得老闆愛 XD 

回過頭來,有人的地方就有政治,我覺得 彼得原理 可以提醒如何審視週遭人的現狀,為何有人明明很聰明,為何做事的成效不佳,在沒有利害關係下,可以當作很有趣的現象觀察。當然,有工作前後關係時,就會是個痛苦 XD 而書裡最後提到的章節,也等於讓讀者在審核自己的身心狀態,因此,假設這本書的養分有 100 分的話,我大概只需要其中的 20-30 分而已,比較好玩的是在教導如何避免不適任的現象,像是讓不適任的員工去做他們適任的工作內容,甚至避開衝突,只是對外商企業來說,除非有老闆愛,不然砍人是比較常見的解法 XD 

2020年11月6日 星期五

[Linux] 使用 uClibc-ng 製作 x86_64 開發測試環境,以編譯 curl / libcurl 為例 @ Ubuntu 20.04

由於硬體要限縮資源,這時會把常見的 glibc 換成 uclibc 節省空間,而過程包括要建置 toolchain 等等。由於部分程式在 embedded linux 運作不如預期,大家擔心可能是 uclibc 產生的問題,就出現在 x86_64 運行 uclibc 作檢驗的念頭

實作上也是要建置 x86_64 的 toolchain 出來,此例就用 curl / libcurl 為例。此篇工作記錄參考 编译工具链制作-x86_64-linux-uclibc 文章,建議先拜讀這篇,裡頭編譯參數的項目都有說明,佛心啊。此處筆記是設法把開發環境都用最新環境,包括:

  • Ubuntu 20.04.1 LTS server
  • uClibc-ng-1.0.36
  • binutils-2.35
  • gcc-9.3.0 
  • mpfr-4.1.0
  • mpc-1.2.1 
  • gmp-6.1.2
  • linux-2.6.39

而後面編譯 curl 採用以下資源:

  • tiny-curl-7.72.0
  • zlib-1.2.11
  • openssl-1.1.1h 

採用 gcc-9.3.0 的主因是 ubuntu 20.04 預設 apt install gcc 就是 9.3 版。以下連續動作:

$ wget https://ftp.gnu.org/gnu/binutils/binutils-2.35.tar.gz
$ wget https://ftp.gnu.org/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.gz
$ wget https://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.bz2
$ wget https://ftp.gnu.org/gnu/mpc/mpc-1.2.1.tar.gz
$ wget https://ftp.gnu.org/gnu/mpfr/mpfr-4.1.0.tar.gz
$ wget http://ftp.ntu.edu.tw/linux/kernel/v2.6/linux-2.6.39.tar.gz
$ wget https://downloads.uclibc-ng.org/releases/1.0.36/uClibc-ng-1.0.36.tar.gz

並全在 user 家目錄解壓縮,解壓縮後把 open source 包都移到 resource 目錄中保存:

[email protected]:~$ tree -L 1 
.
├── binutils-2.35
├── gcc-9.3.0
├── gmp-6.1.2
├── linux-2.6.39
├── mpc-1.2.1
├── mpfr-4.1.0
├── resource
└── uClibc-ng-1.0.36

安裝一些工具:

$ sudo apt install make cmake gcc g++ libncurses-dev libncurses5-dev
$ gcc -v
...
Thread model: posix
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) 
$ g++ -v
...
Thread model: posix
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) 
$ make -v
GNU Make 4.2.1
Built for x86_64-pc-linux-gnu
$ cmake --version
cmake version 3.16.3

建置環境變數,預計都安裝在家目錄的 tarball 裡,此例就是 /home/user/tarball:

$ cat ~/uclibc-init.sh 
export ARCH=x86
export TARGET=x86_64-linux-uclibc
export PREFIX=/home/user/tarball
export TARGET_PREFIX=$PREFIX/$TARGET
export PATH=$PREFIX/bin:$PATH

 

$ source ~/uclibc-init.sh
$ mkdir -p $TARGET_PREFIX

編譯 binutils 環境:

$ cd binutils-2.35 && mkdir -p $TARGET && cd $TARGET
$ ../configure \
--prefix=$PREFIX \
--with-lib-path=$PREFIX/lib:$PREFIX/lib64 \
--disable-multilib \
--disable-werror \
--disable-nls \
--enable-gold \
--target=$TARGET
$ make && make install

 

$ x86_64-linux-uclibc-ld --verbose | grep SEARCH
SEARCH_DIR("/home/user/tarball/x86_64-linux-uclibc/lib64"); SEARCH_DIR("/home/user/tarball/lib"); SEARCH_DIR("/home/user/tarball/lib64"); SEARCH_DIR("/home/user/tarball/x86_64-linux-uclibc/lib");

這邊增加 --enable-gold 用於 gcc-9.3 的編譯時會踩到的需求,通常就是缺一些 header files 就是要找找看躲在 binutils, uclibc 等等

安置 linux header files:

$ linux-2.6.39/
$ make ARCH=$ARCH INSTALL_HDR_PATH=$TARGET_PREFIX headers_install

編譯跨平台 gcc 工具:

$ cd gcc-9.3.0/
$ ln -s ../mpc-1.2.1/ mpc
$ ln -s ../mpfr-4.1.0/ mpfr
$ ln -s ../gmp-6.1.2/ gmp
$ mkdir -p $TARGET && cd $TARGET
$ ../configure \
--prefix=$PREFIX \
--with-sysroot=$PREFIX \
--with-local-prefix=$PREFIX \
--with-native-system-header-dir=/$TARGET/include \
--enable-languages=c,c++ \
--enable-linker-build-id \
--disable-multilib \
--disable-werror \
--without-headers \
--with-newlib \
--disable-shared \
--disable-nls \
--target=$TARGET
$ make all-gcc
$ make install-gcc

安置 uclibc 相關檔案們(startfiles/headers):

$ cd uClibc-ng-1.0.36
$ make menuconfig
Target Architecture
    --> x86_64
Target Architecture Features and Options
--> [*] Enable full C99 math library support  
--> [*] Enable C99 Floating-point environment
--> Linux kernel header location (/home/user/tarball/x86_64-linux-uclibc/include)
General Library Settings
--> [*] Dynamic linker stand-alone mode support
--> Thread support (Native POSIX Threading (NPTL))
--> [*] utmpx based support for tracking login/logouts to/from the system
--> [*]   utmp support (XPG2 compat, SVr4 compat) 
--> [*] Enable SuSv3 LEGACY functions
--> [*] Enable SuSv3 LEGACY macros
--> [*] Enable SuSv4 or obsolescent functions
Advanced Library Settings
--> [*] Enable SVr4 deprecated functions
Networking Support
--> [*] IP version 6 support
--> [*]   Use netlink to query interfaces 
--> [*]     Support the AI_ADDRCONFIG flag
--> [*] DNS resolver functions
String and Stdio Support
--> [*] Wide Charater Support
Big and Tall
--> [*] Support the wordexp() interface
Libray Installation Options
--> uClibc runtime library directory(/home/user/tarball/x86_64-linux-uclibc)
--> uClibc development envirment directory(/home/user/tarball/x86_64-linux-uclibc)
Development/debugging options
--> Cross-compiling toolchain prefix (x86_64-linux-uclibc-)
--> (-D__NR_setns -D__NR_syncfs) Extra CFLAGS
$ make startfiles
$ PREFIX="" make install_startfiles
$ PREFIX="" make install_headers

其中我開啟了不少項目都是後邊在編譯 gcc-9.3 和 curl 會用到的項目,像是缺 ifaddrs.h 而開啟 Support the AI_ADDRCONFIG flag 又相依要開啟 Use netlink to query interfaces 選項等等。過程常見的都是編譯期間缺 header files 而反覆在 uclibc 開啟資源,可以把缺的 header files 在 uclibc 中靠 find 或 grep 搜尋

繼續編譯跨平台 libgcc:

$ cd ~/gcc-9.3.0/$TARGET
$ make all-target-libgcc
$ make install-target-libgcc

編譯和安裝 uclibc :

$ cd ~/uClibc-ng-1.0.36/
$ make
$ PREFIX="" make install

最後一次 gcc 編譯:

$ cd ~/gcc-9.3.0/$TARGET
$ ../configure \
--prefix=$PREFIX \
--with-sysroot=/ \
--with-local-prefix=$PREFIX \
--with-native-system-header-dir=$TARGET_PREFIX/include \
--enable-languages=c,c++ \
--enable-linker-build-id \
--disable-libsanitizer \
--disable-multilib \
--disable-werror \
--disable-nls \
--target=$TARGET
$ make all
$ make install

如此,就可以有基本的環境運行了,例如編譯 main.c:

$ cat main.c 
#include <stdio.h>
int main(int argc, char *argv[]) {
        printf("Hello\n");
        return 0;
}
$ x86_64-linux-uclibc-gcc main.c 
$ ./a.out 
-bash: ./a.out: No such file or directory
$ file a.out 
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld64-uClibc.so.0, BuildID[sha1]=c243334b7550adf7ef5342019c444d6762da738b, not stripped

 

$ ~/tarball/x86_64-linux-uclibc/lib/ld64-uClibc-1.0.36.so --library-path /home/user/tarball/x86_64-linux-uclibc/lib/:/home/user/tarball/x86_64-linux-uclibc/lib64:/home/user/tarball/lib ./a.out
Hello

緊接著編譯 curl 來用,因為他相依 openssl, libssh2, zlib ,所以過程要先把這些函示庫都編好:

$ wget https://zlib.net/zlib-1.2.11.tar.gz
$ tar -xvf zlib-1.2.11.tar.gz && cd ~/zlib-1.2.11
$ CC=x86_64-linux-uclibc-gcc  CXX=x86_64-linux-uclibc-g++  ./configure --prefix=$PREFIX
$ make && make install  

 

$ wget https://www.libssh2.org/download/libssh2-1.9.0.tar.gz
$ tar -xvf libssh2-1.9.0.tar.gz && cd libssh2-1.9.0
$ CC=x86_64-linux-uclibc-gcc  CXX=x86_64-linux-uclibc-g++  ./configure --prefix=$PREFIX --host x86_64-linux-uclibc
$ make && make install

 

$ wget https://www.openssl.org/source/openssl-1.1.1h.tar.gz
$ tar -xvf openssl-1.1.1h.tar.gz && cd ~/openssl-1.1.1h
$ CC=x86_64-linux-uclibc-gcc  CXX=x86_64-linux-uclibc-g++ ./config --prefix=$PREFIX -latomic no-threads no-shared no-rc5  enable-camellia  enable-mdc2 no-tests no-fuzz-libfuzzer no-fuzz-afl zlib
$ make && make install

 

$ wget https://curl.haxx.se/tiny/tiny-curl-7.72.0.tar.gz
$ tar -xvf tiny-curl-7.72.0.tar.gz && cd tiny-curl-7.72.0
$ mkdir build && cd build
$ CC=x86_64-linux-uclibc-gcc CXX=x86_64-linux-uclibc-gc++ cmake .. -DCMAKE_PREFIX_PATH=~/tarball -DCMAKE_INSTALL_PREFIX=~/tarball
$ make && make install

 

$ /home/user/tarball/bin/curl
-bash: /home/user/tarball/bin/curl: No such file or directory
$ ~/tarball/x86_64-linux-uclibc/lib/ld64-uClibc-1.0.36.so --library-path /home/user/tarball/x86_64-linux-uclibc/lib/:/home/user/tarball/x86_64-linux-uclibc/lib64:/home/user/tarball/lib  /home/user/tarball/bin/curl --version
curl 7.72.0 (Linux) libcurl/7.72.0 OpenSSL/1.1.1h zlib/1.2.11 libssh2/1.9.0
Release-Date: 2020-08-27
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS HTTPS-proxy IPv6 Largefile libz NTLM SSL UnixSockets

收工!

2020年11月1日 星期日

[動漫] 鬼滅之刃 一口氣看完 26 集動畫和 205 話漫畫

鬼滅之刃

沒想到幼稚園萬聖節可以看到 禰豆子 裝扮,拿到的小禮物又是這動漫。於是乎,就毛起來來了解!這動漫風格感覺應該是國中以上的分級吧 XDD

若要說第一次接觸是 Yahoo 首頁右邊旁的影片推薦,那時我有印象,某天晚上滑過去看了一下野豬跟主角第一次碰面在打鬥,不小心我也看完全部 XD 打鬥場面對男生真的有吸引到,但我那次也只是閒著才看,完全沒印象是啥卡通,只覺得怎麼那麼血腥。

後來這週末一口氣先在愛奇藝看了 26集動畫,用兩倍數花不到300分鐘就看完了!撐過去並適應血腥風格後,果真故事內容很吸引人的,看完動畫才發現目前上映的動漫電影 無限列車 非常有規劃的串在一起!不過我的作息太難去看電影了,於是乎,我開始看漫畫了。漫畫大概就略過前53話動畫的以描述的內容,從無限列車任務開始看起。

將近 150話的漫畫,大概看了有6小時吧,看完後很佩服作者的安排,意猶未盡,可能熬夜快速地看也有點對戰鬥內容麻痺 XD 反而沒有很享受後期大量的打鬥 XDD 

原本想收藏漫畫的,台灣全21冊配合雙11活動,一線線上購物平台只賣 1600 左右,可是家裡空間好像有點不足啊 Orz 透過鬼滅之刃讓人想起小時候老爸出差時,買了幾本漫畫給我們看,那時都隨便買兩三本單行本漫畫,如亂馬二分之一、幽遊白書第八冊,甚至一些尚不有名的作者畫得全一冊等等,真是懷念小時看漫畫的單純感。

2020年10月21日 星期三

處理 Adsense 政策中心 帳戶問題 廣告放送量受限 無效流量疑慮

Adsense-政策中心-帳戶問題-廣告放送受限-無效流量疑慮

負責幫公司經營流量變現不知不覺也三年了?時間過得真快。這個題目說真的是好玩的,運作原理不複雜,但變化無窮。如何幫公司從流量轉換現金是需要許多智慧的。這個題目也是任何做服務的終極目標,如何產生現金流來養活團隊!

近一年半來,月初結算才能發現上個月被扣了多少無效流量,緊接著要去拆解為何導致這個現象。這點一直以來都沒標準答案,尚未深入此領域的同事,總是認為這一定要有標準答案才對,殊不知 Google Adsense 就是不告訴你為什麼,連 Adsense 客服也不會跟你說,好心一點只會模糊的指導,甚至像遊戲 NPC 一樣,永遠只會說固定的對話。因為告訴你怎樣判斷無效流量後,有心人士就拿它吃 Google 豆腐的,這也是一條完全沒有標準答案的生活模式,趣味無限。

這幾年累積的經驗,常見處理無效流量的方式:

  • 降低廣告被誤點
  • 避免手機內 webview 被過早初始化(如 android TabHost 在背景初始化了網頁及 adsense 內容)
  • 避免在無使用版權的內容網頁上投放廣告(無內容)
  • 遵守 Adsense 廣吿政策

由於本就正派經營沒在買流量,一直認為無效流量的判定大多是 Google Adsense 誤判(過去也曾收過 google adsense 事後補償),直到最近在政策中心直接顯示有帳戶問題,廣告放送量受限。這招很恐怖,一刀見骨不給廣告投放。跟之前面對的無效流量不同,以前是正常拿到廣告來投放,月底在減去無效投放。而這次是一開始就不給廣告,涵蓋率約 2% 水準。

解法呢?回歸原理,除了重新檢視廣告投放是否符合 adsense 政策外,請記得多去思考無效流量的定義,真的快走到冥想階段 XD

Adsense - 營利和廣告 - 無效流量的定義

ref: https://support.google.com/adsense/answer/16737?hl=zh-Hant

當頭棒喝的句子:發布商仍有義務確保自己的廣告流量有效!

一直以來都正派經營,完全忘了無效流量可能來自既有的客戶,甚至有心人士!因此,需要花心思去抓可能的兇手,這時就回歸自身廣告投放的情境是如何,規劃怎樣抓出異常行為。

最後,我偷懶設計了一些 GA event 且設法紀錄 IP 資訊(例如把 IP 記錄在 event label,正規一點應當掃 web server access logs)。透過簡單的機制,就可以發現異常行為。例如期待正常的使用者一週使用服務最高只會瀏覽 50次,那單週超過百次的就很有可能是異常。再搭配 ipinfo.io/ip 去察看基本資訊,有時會找到一些相關訊息。對症下藥實現 IP 黑名單管理法,該名單內的 IP 在使用服務時就不會投放廣告,就能達成自身過濾無效廣告流量。

最終無效流量議題,仍回歸到避免在無效用戶上投放廣告!每一則廣告都是買家的錢, Google 要確保廣告投放是有意義的,這樣買家才會繼續在 Google Ads 買廣告,而邪惡的事件就是...有心人士可以故意一直去點擊廣告消耗掉別人(競爭對手)廣告預算。

我想,這無效流量的戰鬥還是會持續下去的。

2020年9月26日 星期六

塑膠年年的 塑膠黏黏的 老化氧化的解救方式

塑膠 黏黏 老化 氧化 處理方式

以前買到一個螢幕沒有內建喇叭時,買了一個飛利浦特價的藍牙小音箱,當時覺得外型還滿可愛的就下手了。隨著時間,已經被我晾在旁邊兩年了,原本想拿來跟投影機搭配,卻發現整個機身黏到不行又沾滿灰塵。透過用膠帶沾黏處理,還是沒解決。甚至一度想說拿膠帶包一身算了 XD

後來,還是敲敲關鍵字研究一下大家的解法,有很多種,其中應景的解法就是家家戶戶在 COVID-19 疫情下都早已配有酒精!只需拿乾淨的小布,用酒精噴濕拿來擦拭機殼,即可解決。

在研究資料時,也才發現常見的環保塑膠會隨著時間如此,因此有些3C產品會進行特惠清庫存,以免買家拿到後,整個黏到不行而退貨

2020年9月18日 星期五

台灣社群行銷年會 2020 團拜

台灣社群行銷年會 2020

今年報名參加 taiwan social marketing 2020 活動,擴充一下自己行銷領域知識、順便建立關鍵字索引,沒想到這場有 2800 人!記得上一次來這種地方好像是 n 年前的 Awsome Day,每次來都背著筆電,結果筆電都沒用到,接著不知中午要吃啥,輪迴著每次都想吃超商卻又發現超商要走很遠,然後隨便找一間吃完,才想起來旁邊不是有世貿餐廳,不是有 101 美食街,不是有 ATT4Fun !! 這次活動的彩蛋大概是認識旁邊的高手,哈拉了幾句,受益良多。

回過頭來,說一下會讓我期待的題目是 PopDaily 波波黛莉的異想世界 近況。近幾年我也在負責流量變現,有觀察到 Alexa.com 的 Top Sites in Taiwan 的排名變化,因此對這間感到興趣。雖然我還是把他歸類在內容農場的思維,但近幾年內容農場已經開始收斂,而透過 copy 思維的產文方式在 SEO 或 Social Network 已經佔不到什麼便宜,還活著服務絕大多數都進入 PGC 領域,因為 UGC 水準很難控制,沒有分潤機制也吸引不到別人貢獻,所以,挽起袖子,自己養!

很高興聽到 PopDaily 在 2017 年起開始軸轉,朝向經營 PGC 社群。我記得前幾年有關注到公司資本額的變化,那時以為單純賣掉公司,這次聽講才發現,公司經營者很大膽的向股東們論述未來走法,將全部利潤再投入經營公司核心,陸續在 2020 年初也看到在各個常見的工程粉絲團張貼聘請一些數據分析的職缺,非常努力地往前邁進。

再次回顧,再次敬佩這間公司。

接下來記憶深刻的是 TikTok 吧!原先一直覺得年紀已大,跟不上時代。近半年還是設法安裝 TikTok/小紅書/IG/Snapchat ,強迫自己一週播一點時間去滑幾下。後來看來看去核心都是 TikTok 機制。我認為 TikTok 營造出讓眾人可以快速/低門檻的加入表演,透過樣版/快速上手模仿,讓整個社群瘋狂起來,甚至濾鏡什麼都不是太重要的開始。

令我驚訝的是 TikTok 原來跟中國境內的抖音模式不同,抖音有直播帶貨等服務變現機制,而 TikTok 沒有這種極度噁心的機制(未來說不定也會有?畢竟網購經營還是最佳的變現手段)。跟創作者沒有分潤機制,但在台灣沒推廣的機制下,還是產生了 400萬 MAU 呢!甚至在推廣未來想找資料,就上 TikTok 搜尋 hashtag!!

TikTok 關鍵數字:

  • 每月有 700萬支影片
  • 平均一人一天會打開 8 次 app
  • 平均一人一天會觀看 45分鐘!!
  • 請重視影片觀看數,而非粉絲數,就算沒有粉絲的,也很可能靠推薦機制讓很多人看到
  • 用戶其實是先看推薦影片,而非追蹤的用戶

此外 TikTok 的核心還是跟推薦系統脫不了關係,正是從內容農場起家的今日頭條,透過收集大量有趣的事物,再搭配 SEO 跟推薦系統瘋狂搶佔眼球。

剩下跟本業相關的,其實是 91APP ,畢竟自身就是在公司負責數據收集/系統建置/服務變現相關應用,只是隨著年紀的增加,越來越能體會何謂團體戰!就像其他講者公司也都採用 91APP 的方案,賺錢的公司應當關注自己的核心事業,把系統整合的交給專業的來 XD 不要太貪心都自己做,這樣可以切割風險管控,更可以專注在自己的強項。

其餘演講內容也都很精實,但跟我的本業就相距較遠,像是明星代言的成本跟效果、直播帶貨等等。但對岸基本上是網購/直播的大聯盟等級,其實已經有一些翻出牆外再說書的 Youtuber 可以略知一二,有興趣可以看看:


可以觀看一下此活動的 社群行銷年會 舉辦者龍哥的 影片剪影,感受一下 2800 位聽眾的氣勢。其餘的課程文字筆記,可以瀏覽社群的文字記者,都還滿仔細的:

2020年8月16日 星期日

[遊記] 2020年 台北 101 觀景台 預約/特價 一人 150元 攻略

這次使用 KLOOK客路 系統去訂購 台北 101 景觀台 門票,國人限定門票,特價 150 元!非常便宜。原先是 2020年 國人優惠票價 成人300元(可帶2位12歲以下孩童):

然而,又在六月到九月期間,多開放了 150 元成人票:2020/06/13 - 09/30 ,前六百名還有 B1 美食街手搖飲品一杯。活動網址:https://www.taipei-101.com.tw/tw/event/content/ec029bfb-d7e5-4e76-812c-100bb72772f4

但也因為如此,整個台北101景觀台入場變得非常非常擁擠。像現在的暑假期間,週五時段中午12點去排隊,可是要排一個半小時才上得了台北101景觀台。

台北101景觀台現場購票/網路訂票驗票

非常推薦參加第一梯,例如週日時段第一梯 10:00 ,只需時間抓好,大概 09:45 到台北101捷運站,接著出捷運站後,立刻從左邊搭電扶梯上一樓,從台北101購物中心一樓進去。此時已經看到不少人正在排電梯,可以選擇直接走手扶梯到四樓,接著經過購物中心四樓大平台後,在經過手扶梯到五樓,即可排隊進場。

台北101購物中心五樓-景觀台入場排隊

此時排隊分成左右兩邊,左邊是網路購票,右邊是現場購票。透過網路購買可以方便許多,人潮大概是 1/4 的現在購票人潮(中午時段網路購票的可能非常接近現場購買人潮),現場購票幾乎可以排到安全梯了!這不是誇飾是真實的人潮 XD

以週日 10:00 第一梯來說,速度快的話,大概 10:30 前就能上台北101觀景台,大概在五樓排隊15分鐘檢驗網路訂票跟身份檢查(國人優惠,要帶證件),另外5-10分鐘再等電梯。可以搭到31秒就到89樓的快速電梯。

上去台北101景觀台,就自行體驗樂趣囉!可以看到雪山隧道,以及目前全台房價最貴一戶號稱18億的大樓,還可以看到像 Macbook 筆電蓋的 Apple 專賣店!

可以的話,花點小錢享受一下全台最高的雞蛋糕(滿好吃的),以及買杯咖啡/冰淇淋(有折價券, 一張票可折50元),這些都可以稱作全台最高享受了!最後在到91樓戶外看一下,再去 88樓阻尼器,就可以準備在排隊下樓了 XD 週末10點進場,很有機會符合前600名規則,最後還可以拿到 B1 飲料兌換券一杯

2020台灣最貴房樓, Apple 專賣店

只是整個行程可能有大半時間在排隊就是了。

2020年7月18日 星期六

越過山丘、喋喋不休



難得的一夜,被老大抓住一起 debug 公司產品的一些現象,從 21:00 弄到隔了一天。雖然老大是在跟我抱怨著怎會發生這種奇怪的事,我反而很感謝有這麼勤勞的老大,哈哈,老大已經連續四百多天每晚都要設置公司關鍵產品的 night run 呢!

最近有很多感謝,感謝在這個年紀,還可以有幾位死黨閒話家聊,天天牢騷個幾句,都把人拉回到像大學宿舍那般的天不怕地不怕,明明此刻的我們,還在苟延殘喘,努力著找尋那一丁點的機會翻身?彼此分享著肩頭的重擔、丟幾本書、買著些款3C,體驗著彼此對市場的觀點,不亦樂乎!

感謝能跟著幾位不到三十歲的同事交流想法,才發現自己已有歲數而且很愛碎碎念 XD 滿好的,也讓我想起創業有成的表弟,也曾跟我哈拉自己快管不動員工了 XDD 只能說世代的交替,也開啟尊重和持續交流的舞台。想起二十多歲時,那時跟同事彼此較勁著對未來發展的進行方式,彼此用著不同的信念去闖闖,很宅的每天19點就開始埋頭 coding 去珍惜舞台。雖然走法不同,也各自精彩。我想,就像現在看著年輕朋友,用欣賞取代碎念著他們的熱血就好 XD 生命自會尋找舞台的!

時間永遠靜靜地流逝著,翻本書挺好的,看場懷舊電影也好,跟死黨碎念著八卦,再跟年輕朋友要一點熱血,讓自己不要老那麼快。或許,我更該做的是當年二十多歲時,看著三十多歲的同事,學著他天天翻一下小說和漫畫,莫忘無限想像。

2020年7月12日 星期日

[電影] 再看一次「Ready Player One / 一級玩家」


圖片來源:維基百科

用 Shopify 聽個 Ready Player One Soundtrack 吧!

沒想到這部電影已經兩年了!再次觀看又有不同的悸動。原本只是用 NETFLIX 想找個小孩看的電影,不小心就重新溫習了這電影。

此時此刻,特別對第一關的那幾句話很有感觸:

Why can't we go backwards, for once?

Backwards, really fast.
Fast as we can.

就像眷戀著過去一樣,很妙的句子!

後來,主角面對著虛擬與現實的交錯,無意間在虛擬世界曝光了現實資訊。更讓人回想起自身,是不是有太多無效的社交正不斷的侵蝕我們的時間?我們卻還在那邊沾沾自喜呢?或是存在著一些人際交際,是不是很想回到過去呢?

對此時的我而言,再看一次電影,有了更不一樣的體悟,真是令人想不到的收穫

2020年6月20日 星期六

[Javascript] Shopify App - 修正 location.replace 與 GA 追蹤碼 utm_* 的應用

認真使用 Shopify 大概也有一個多月了,使用了一些付費 Shopify App 在做本地化加強,發現這些第三方 app 還滿善用 variant 機制,原先是拿來設計一個商品有多種顏色等用途,現在則是應用在多儲存一些在地化的資訊。

因此,當發現需要轉化成在地化資訊時,會透過接近導網址架構,多添加 variant 資訊:

var new_url = window.location.protocol + '//' + window.location.host + window.location.pathname + '?variant=' + variant_id;

然而這樣的小機制,卻把 utm_* 的追蹤碼都給去掉了 XD 所以在幫他補強一下:

var new_url = window.location.protocol + '//' + window.location.host + window.location.pathname + '?variant=' + variant_id;

if (window.location && window.location.href) {
var m = window.location.href.match(/[\?&]([^=]+=[^&]+)/g);
if (m) {
var params = null;
for (var i=0; i<m.length ; ++i) {
if (m[i].indexOf('utm_') > 0) {
if (params == null) {
params = m[i].substr(1);
} else {
params += '&' + m[i].substr(1);
}
}
}
if (params != null) {
new_url += '&' + params;
}
}
}

收工!

2020年6月7日 星期日

[PHP] 解決 CodeIgniter 4 Command line 輸出緩衝/不即時的問題 (ob_start / ob_end_flush) @ macOS 10.15

有點忘記了,好像在 CodeIgntier 3 (CI3) 建置 Command line 運行的任務時,可以很即時看到運算的輸出結果(預設是 testing mdoe?),但在 CI 4 時,卻必須等到所有任務都做完了,才會一次輸出結果。這對於在 Jenkins 系統運行時,無法時看到運行過程是很不方便的。

追蹤 CI 4 的架構,起因是 system/CodeIgniter.php:run 中,運行時呼叫了 Events::trigger('pre_system'); ,在 app/Config/Events.php 可以看到定義了 pre_system 的工作內容,大意就是當 ENVIRONMENT 不是 testing 時,就會開啟 ob_start 使用機制。

在 CI 架構上,使用 ob_start 也有個好處,那就是寫一些網頁服務時,有時是內容產出跟 header 送出夾雜在一起。當在 PHP 使用 header 送出資料時,是不允許前面已經輸出過資料的。這時靠 ob_start / ob_end_flush 應用,就可以完美排除這種問題。

回過頭來,面對 command line 的任務,若想要排除 ob_start 的影響,可以試著包裝一個輸出訊息的函式,並在裡頭輸出完資料後,再靠這兩段補強輸出資料,如:

function debug($message) {
echo "[DEBUG] $message\n";
while (\ob_get_level() > 0)
\ob_end_flush();
}

或是乾脆在負責 Command line 任務的建構子中,直接靠 ob_get_level 和 ob_end_flush 清掉輸出緩衝的設計:

public function __construct() {
while (\ob_get_level() > 0)
\ob_end_flush();
}

如此就可以得到即時的輸出資料了

2020年6月2日 星期二

[Linux] AddTrust External CA Root expired 處理方式 @ Ubuntu

週日也不幸踩到這個雷了,當時只解了幾個,沒想到這個範圍很大很大。
故事是我們採用 Namecheap 服務來簽署 Wildcard SSL 憑證,而使用到 AddTrust External CA Root 簽署。

當 AddTrust External CA Root 有效期間只到 2020/05/30 10:48:38 時,時間一到後,導致用戶連到我們的服務產生失敗,以 curl 來說,他預設依賴 OS 提供的系統憑證資訊:

$ curl https://ourservice.exmaple.com
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

而 Chrome Browser 很佛心,從軟體層自動幫轉到 USERTrust RSA Certification Authority。

問題的解法:

正確解法:直接再重新簽核新的憑證,新的憑證簽署時會略過已過期的項目。
臨時解法:將 https client side 的 AddTrust External CA Root 註解起來 

臨時解法,以 Ubuntu 內的 https client 為例,將 mozilla/AddTrust_External_Root.crt 那行註解起來

$ sudo vim /etc/ca-certificates.conf && sudo sh -c 'apt update && apt install ca-certificates && update-ca-certificates -f -v'

臨時解法只能解決 https client 是已知的範圍,像是服務的 client 都是自己控制的,這時就可以先解決那些 client 憑證的檢驗機制,但追到更細時,大部分的 https client 靠 OS 提供的憑證資訊就只要改一次,如果 https client 上跑 node.js 等應用,他們可能會依賴其他相關套件來維護憑證資訊,不一定會用 OS 內紀錄的憑證資訊,這時要排除會非常痛苦。因此,正確解法是重新簽發憑證

2020年5月19日 星期二

Shopify 開店筆記 - 跨境電商、依重量計算運費、依地區限制購物

google SHOP stock 2020/05/16

約莫協助公司維運自家電商已經有五年多。

通常電商的成績不佳,大部分會被認為是電商平台的設計問題,像是購物流程是否可以改善、網站配色是否不夠精美等等。因此,我們也開始研究是否朝向知名的網購平台 Shopify 來維運。Shopify 是一個體質非常好的電商平台,非常適合小試身手的平台,而這波疫情也讓 Shopify 股價扶搖直上。

其實,公司維運的電商平台老早就有用 Shopify 幾年了,如幫其他客戶經營副牌、白牌等。對我而言是一個熟悉又陌生的平台,因為整體上的維運都已經不需要透過工程師。趁這次把品牌電商遷移到 Shopify 上,順手記錄碰到的課題,主要分成收錢(金流)、寄貨(物流)、買家交易過程和其他需求等筆記,而 Shopify 其實有不錯的 Shopify App Store,可以自行添購(通常是訂閱式,每月支付),有些真的滿夠用,十分方便。

收錢/金流:

提供常見的 Paypal 支付,以及 Shopify Payment 機制,而這兩點都恰好因為已有 Paypal 帳號跟有香港辦公室的關係無痛打通。這兩個金流的使用都有地緣限制,像 Shopify Payment 還可以有 Google pay / Apple Pay ,只是不見得買家的地方可以使用。

金流上當客戶在網站上完成支付後,Shopify 便會立即發信給 商店聯絡電子郵件,就是在 "後台 -> 設定 -> 一般 -> 商店聯絡電子郵件" 設定。

此外 Paypal 帳號也是只限制 PayPal 商業帳戶,不能用個人的。

寄貨/物流:

Shopify 的物流可以簡單到自己寄送即可,單純把訂單標記成已出貨,並且可以填寫 tracking code 及查詢的方式(使用的物流),就完成了!

而物流的複雜度其實可大可小,我認為比較重要的是運送費率的設計,Shopify 支援設定多個倉儲位置,假設設定了兩個處(香港和台灣),那必須替這兩個倉儲分別設定運送策略。假設只對香港設定寄送到全世界而台灣倉庫沒有任何運送設定,這時有一位台灣用戶下單時,他會被默認靠近台灣而使用台灣倉庫的寄送策略,但台灣倉庫沒設定運送到台灣的運費規則,而導致使用者無法完成下單(會顯示類似無法寄送的錯誤訊息)。

另外,若設計以重量方式計算運費時,假設以 0.5kg 為間隔單位,分別設定了 "小於0.5kg"、"0.5kg到1.0kg"、"1.0kg到1.5kg",1.5kg到2.0kg",這時若使用者買的東西總重落在 2.0kg 以下是可以的,且有運費合併計算的美意,但如果使用者買的總重量超過 2.0kg 時,一樣會落入無法寄送的錯誤訊息,因為沒有大於 2.0kg 的運費規則。

另外,也要留意裝箱的箱子重量,定義了依重量計費時,結果不如預期時,可能是箱子重量影響。

買家交易過程:

如果沒有調整過預設設定時,客戶在下單過程時,可以選用手機門號簡訊完成認證,快速驗身完就可以下單了,非常貼心。而訂單狀態會用簡訊通知對方。然而,若是在做跨境電商時,碰到訂單問題時,就得走電話聯繫,推論會十分痛苦。另一方面,快遞都會要求填寫聯絡電話,若客戶訂購時沒留下電話資訊也會很苦的。

因此,將會到 "後台 -> 設定 -> 結帳",在 "客戶聯絡" 方面,改成限定用 email 結帳。表單資訊,會要求填寫 "運送地址電話號碼"。作為跨境電商的訂單管理機制。

其他項目 - 賣樣品給客人:

Shopify 建立訂單草稿 是非常好用的項目!若業務想要銷售樣品給客人時,透過訂單草稿,可以自行打包幾個商品、給予折扣,再透過寄送電子發票,便直接通報客戶直接到 Shopify 付款,整個過程非常方便。讓業務很快地辦完事,負責金流物流的同事也只是例行處理自己的職責。十分完美。

可以多多參考 Shopify 的說明:訂單草稿

其他項目 - 提醒未完成結帳項目:

當用戶在 Shopify 平台下單時,未完成結帳的訂單,一樣在後台可以看到。Shopify 可以在 "後台 -> 設定 -> 結帳 -> 未完成結帳作業" ,設定幾小時後傳送提醒信件。如此可以設法拉回一些潛在用戶。

其他項目 - 跨境電商 - 依照地區給予不同價格:

這個應數主要是總代理跟代理之間的關係,如果某個地區有代理商了,總代理賣的價格通常不能低於代理商,不然代理商都賣不出貨了。面對這個需求有兩種解法,第一種就是故意在電商上不銷售到有代理商的區域,可以靠 運費費率 的設定,讓有代理商地區沒有運費規則,這樣用戶就不能下單。另一種要花點錢的機制,使用 Multi-Country-Pricing 等類似服務,依照地區給予不同價格,但 Shopify 的核心只有收一個幣別,就算透過 Multi-Country-Pricing 的機制能夠給予當地使用者觀看當地的幣值,最終在收費時,還是會轉回原先 Shopify 商店所規範的價格(如美金為單位)


其他項目 - 信件通知:

Shopify 會用 "後台 -> 設定 -> 一般 -> 商店詳細資訊 -> 客戶電子郵件" 當作 mail sender 發送。通常 Shopify 發送到自己管理的 mail server 時,可以透過 SPF record 來允許,增加信件不被規範到垃圾信中。只有時很慘的,自家 mail server 阻擋了 Shopify 寄來的信時,只能設法靠不同的 mail domain 來避開。


在不同 mail domain 避開的機制下,例如現在用 [email protected] 作為 商店聯絡電子郵件 ,只好再用 [email protected] 當作 客戶電子郵件 。此時必須記得去修改信件通知的資訊,避免信件通知信一直請客人寄信給 [email protected] 尋求客服。而 [email protected] 最好也在設定個自動轉寄到 [email protected] ,避免漏掉客戶的重要信件。願意花錢可以靠 AWS WorkMail 單一帳號一個月四美金頂著用,不願意花錢可以試試免錢的 Yandex.Mail 。

2020年5月8日 星期五

[Python] 使用 http.server.BaseHTTPRequestHandler 製作簡易 Proxy 機制 @ macOS, python 3

學會用 curl / wget 模仿一些 request 後,在一些特殊情境上,還是得弄個 proxy 出來,雖然有 man-in-the-middle Proxy: mitmproxy 可以使用,但有時就是想要單純一點,寫點小程式自娛一下 XD

故事的情境:

- 想用 VLC 播放一些 Streaming 來源,但該 streaming 在存取時要求多塞一些request header,直接播會收到 40X 回應
- VLC 預設只吃 OS Level 的 proxy 設定

基於上述情境,雖然靠 curl/wget 添加 request header 可以搞定,但 VLC 這類就無法達成播放指定來源時多添加 request header。

因此,除了靠 OS Level 的 Proxy server 添加 reader header,就剩寫一隻代抓資料,邊抓邊輸出 stream 的小小程式,程式碼:

import http.server
import urllib.parse
import requests
import time

class ProxyHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
	protocol_version = 'HTTP/1.0'

	def do_GET(self, body=True):
		try:
			target_url = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query).get('url', None)
			print("[INFO] target_url: "+str(target_url))
			if target_url != None:
				target_url = target_url[0]
				req_header = self.parse_headers()
				#resp = requests.get(target_url, headers=req_header, verify=False, stream=True)
				resp = requests.get(target_url, headers=req_header, verify="certs.pem", stream=True)
				print("[INFO] resp.status_code: %d " % resp.status_code)
				if resp.status_code == 404:
					self.send_response(404)
					self.send_header('Content-Type','text/html')
					self.end_headers()
					self.wfile.write("NOT FOUND".encode())
				else:
					self.send_response(resp.status_code)
					for k in resp.headers.keys():
						print("[INFO] resp.headers: [%s][%s]" % (k, resp.headers[k]) )
						self.send_header(k, resp.headers[k])
					self.end_headers()
					for chunk in resp.iter_content(chunk_size=1024):
						if chunk:
							self.wfile.write(chunk)
							self.wfile.flush()
						else:
							time.sleep(0.05)
				
			else:
				self.send_response(404)
				self.send_header('Content-Type','text/html')
				self.end_headers()
				self.wfile.write("NOT FOUND".encode())
		finally:
			pass

	def parse_headers(self):
		req_header = {}
		for line in self.headers:
			line_parts = [o.strip() for o in line.split(':', 1)]
			if len(line_parts) == 2:
				req_header[line_parts[0]] = line_parts[1]
		return self.inject_header(req_header)
    
	def inject_header(self, headers):
		headers['Referer'] = 'https://example.com/'
       
		return headers
	

if __name__ == '__main__':
	server_address = ('0.0.0.0', 8081)
	httpd = http.server.HTTPServer(server_address, ProxyHTTPRequestHandler)
	print('http server is running')
	httpd.serve_forever()

如此跑起來後,就可以用 http://localhost:8081/?url=https://exmaple.com/streaming 機制,例如 VLC 軟體直接輸入 http://localhost:8081/?url=https://exmaple.com/streaming 位置來嘗試播放。

2020年5月7日 星期四

Raspberry Pi 3 + RetroPie = 電視遊樂器 - 超任為例 @ macOS

Pi 3 + SNES

之前為了研究音控買了個 Pi 3 搭配 Google AIY Projects Voice Kit ,結果研究完就晾在那邊一年多了吧?反而音控設備已買了成品 Google Home mini 和 小愛音箱。最近無聊就想研究一下樹莓派怎樣玩遊戲,實在是已經不只一次看到什麼月光寶盒再賣,還是嘗試一下好了。

果真有個 RetroPie 就是專門做了超級多的電玩模擬器,裝下就搞定了!有幾件事要留意一下:
  • Pi 3 內建無線網卡,但安裝完卻沒法設定,連 ifconfig 都看不到
    • 解法:裝完 RetroPie 系統後,需要先設定 WI-FI Country ,才能正常設定無線網路
  • 在網路上亂找到的 ROM 檔案,擺入後卻無法被偵測或運行
    • 解法:還是裝一套 PC 版的來驗證吧 :P 有時真的是網路上的檔案格式問題,在 macOS 可以試試 OpenEMU 這套
  • 如何從 PC 傳 ROM 進入 Pi 3 + RetroPie
    • 解法:善用 RetroPie 的環境,可以把 SSH 打開,靠 SCP 或是透過 SAMBA 網路芳鄰進去也很方便
其他動作:
  • 在 https://retropie.org.uk/download/ 下載 Pi 3 作業系統
    • https://github.com/RetroPie/RetroPie-Setup/releases/download/4.6/retropie-buster-4.6-rpi2_rpi3.img.gz
  • 在 macOS command line 連續動作處理(此例 SD Card 位於 /dev/disk4,請不要隨意複製指令執行!)
    • % sudo diskutil unmountDisk /dev/disk4 % unzip retropie-buster-4.6-rpi2_rpi3.img.gz % sudo time dd bs=1m if=retropie-buster-4.6-rpi2_rpi3.img of=/dev/rdisk4 2729+0 records in 2729+0 records out 2861563904 bytes transferred in 506.080069 secs (5654370 bytes/sec) 506.10 real 0.01 user 2.08 sys % sudo diskutil unmountDisk /dev/disk4
  • 當 Pi 3 + RetroPie 啟動後,設定 Wi-fi Country 跟啟動 SSH
    • $ sudo raspi-config
      • Change User Password
      • Localisation Otions
        • Change WI-FI Country
      • Network Options
        • Wi-fi
      • Interfacing Options
        • SSH
      • Update
      • Finish
  • 更新系統以及 RetroPie 眾多資料(模擬器)
    • $ sudo apt update $ sudo apt upgrade $ sudo apt dist-upgrade $ sudo apt autoremove $ sudo apt install tmux vim

2020年5月4日 星期一

重溫超任「三國志3 中文版」

SENS 三國志3 中文版

記得第一次接觸是小學的時候,去幼稚園同學家的遊樂器材店,就這樣初次接觸三國志遊戲。下一次,則是 PC 版的三國志五了!

記得小時候搭配藍截者,有那種重返前一刻狀態的密技,就這樣用新增武將時,用耐心找到單一能力達 100 的員工 XD 此例是君主單純調配能力達到武力 100 ,其餘軍師則是花耐心找到智力 100。

就這樣,配一個君主跟三名武將,從許昌出發,而智力 100 的特色是做事不斷去煩軍師,只要軍師說 ok 就肯定 ok!透過偽書降低對方忠誠度,或是錄用對方都是軍師說好時,一定 100% 成功率的。沒想到這時把玩這款遊戲,一不小心就噴了 3 小時,更讓我想起工作上的瑣事

大概有三件事:

  • 大家特愛當中庸者,什麼都要摸一點,像是能力各個 85 甚至 95,都還不如單一項目專精 100 。單一能力達 100 時,才會進入另一個模式。
  • 單一武將兩個能力很好,不如兩個武將各一個能力好,在這種回合制上,就凸顯出兩個人的好用。例如一個人放偽書,另一個來錄用。因為有時對方身上兵權很重,第一次放偽書,可能下一回合兵權就被去除
  • 數字化,明明很普通的遊戲,卻還是被數字吸引著,養成一種無奈的耐心跟自律,規律地聽著洗腦的背景,時間就消逝了,而成就領地卻增加
這些是恰好跟工作有關,不要當英雄把責任跟任務攬在身上,一個人一天只有 24 小時!要多培養夥伴能力並珍惜單一能力技能很強的夥伴!最後,則是流量變現上持續數據化、遊戲化。

2020年4月27日 星期一

海潮之聲

Untitled

晚上 coding 時,看了一眼 NETFLIX ,看到國高中看過的動畫電影 海潮之聲 ,看著時間才一小時很剛好,就投放到 Chromecast 後,改在電視前面 coding ,也很不錯的在播完前沒多久收尾了 side project。

海潮之聲的故事,說真的記不太得,在看之前,我一直把劇情跟 兒時的點點滴滴 混在一起 XD 雖然我記得著海潮之聲是校園男女故事,但我腦袋裡一直想著海潮之聲有畢業後的同學會,好像在回憶什麼故事似的,看完才發現我真的錯很大 XD 這是一齣很直觀的故事敘述,雖然動畫電影的開頭是個倒敘。

此時此刻,看著這純純的愛情故事,也令人想起跟朋友的交流,記得有位朋友跟我分享起戀情上發生的事,這類通常是屬於訴苦。當時我就分享了我年輕時對於交往的想法,那就像翻開一本書,時而歡樂,時而禁書,又可能是悲傷、開朗的、故作堅強的、萬般神秘的。讓人常常一翻開就背負著一種責任要看完它。

搭配著海潮之聲的劇情發展,真是有所共鳴啊...因為神秘而多問了什麼、因為關心而多付出了什麼,殊不知情感的流露就這般地灑了滿地。

別忘了,要記得學會收個尾囉!

註1:用 NETFLIX 找 海潮 時,不能用兒童角色,這是一部 7+ 動畫電影
註2:在夏天聽原聲帶很應景 - Spotify : The Ocean Waves (Original Soundtrack)

2020年4月24日 星期五

[書] 流量池

Untitled

最初是因為瑞幸咖啡的關係,追了一下 CMO 楊飛經歷,就順手 K 完這本書。

關於瑞幸的故事,可以參考滿多不錯的影片:

【中国商业史09】瑞幸咖啡资本骗局大起底:老板卷走150亿,好兄弟却亏了8000万美金,背后究竟发生了什么?——冲浪普拉斯


CK投資理財|瑞幸咖啡背後的龐氏騙局(看完就全明白了!)|華爾街割韭菜,瑞幸咖啡絕非第一家!


不過,對於 CMO 的貢獻,倒是不可抹滅的。畢竟公司的行銷活動還是扎實的被執行出成績了。

對於這本非常值得番一遍,雖然我自己看得很痛苦 XD

主要是一樣的道理不斷的用其他案例呈述,且經驗多為天時地利人和的八卦屬性,也就是沒那個舞台就算知道了這些經驗,也終將隨著時間而無意義。但只靠一本書,就多看一下別人的眼界,還是很值得的。

最後就把這本書介紹給長輩們翻一翻 XD 恰好有一位長輩對流量變現很感興趣,要我開些書單,結果我亂開 XD 這次看到這本頗有名氣的,既不會太深的技術,也有很多案例,非常適合給未涉入過深此領域的人,嚐鮮一下。

2020年4月13日 星期一

[GAME] NBA 2K20 有趣的 MyGM 球隊經營 @ Xbox One

NBA2K20-MyGM01

因疫情的關係,也開始接觸遊戲,買了好一陣子,這週末才點進去 MyGM 的內容,一看真不得了,把球員跟球團經營的薪資都有曝光 XD 真是不錯!其中球團內的常見職務都有給予一些徽章,例如做好就給好徽章,做不好就給壞徽章。

NBA2K20-MyGM02

其中財務長真的好有感:
好的財務長,透過人脈關係,可以減少 10% 行銷費用
壞的財務長,缺乏人脈關係,增加經營成長 10%
壞的總經理特助,愛跟媒體洩露交易資訊
非常有趣!其實擺在公司內的高管經營也都是很合情合理的,如何不內耗,真是一門學問啊。

想起五六年前踏入創業圈的高中學長,分享當年完成A輪後,就趕緊搭建明星團隊,用錢找高手、名人,以此增加企業經營效率跟估值!可惜的,時也運也命也,有些時機錯過了就沒了。

註:結果我還是沒玩多少 NBA 2K20 XDD 都在玩 FIFA 20 ...

2020年4月12日 星期日

[Linux] Dropbox 缺少的 shared libraries @ Ubuntu 18.04 / dropbox-lnx.x86_64-94.4.384

最近更新 Ubuntu 18.04 的環境,順便重抓 Dropbox 來更新。

https://www.dropbox.com/zh_TW/install-linux
$ cd ~ && wget -O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf -

一執行發現缺了不少 shared libraries ?很怪,印象中以前沒這樣過,順手筆記一下裝了哪些:

$ sudo apt install libxfixes3 libxdamage1 libglapi-mesa libxcb-glx0 libxcb-dri2-0 libxcb-dri3-0 libxcb-present0 libxcb-sync1 libxshmfence1 libxxf86vm1

2020年4月8日 星期三

楓林網 8maple.ru 數據分析

好久沒看到版權事件了 Orz 今天網路界的不小的新聞就是楓林網被抓了,從新聞節錄下來的數據:
  • 兩位台大碩工程師,年收2000萬
  • MAU 3000萬
  • 廣告月收入 200萬
  • 機器月支出 30萬
從 Alexa.com 得知的數據:台灣網站排名掉到 145 左右

01

02

03

新聞上提到有成立廣告行銷公司,假設純靠 Google Adsense 的話,推敲如下:
MAU = 3000萬
廣告收入 台幣 200萬
每天需賺 7萬台幣 => 2333 美金 
若 eCPM 為以下數值
- 0.03 美金 => 廣告曝光 7776萬次
- 0.06 美金 => 廣告曝光 3888萬次
- 0.09 美金 => 廣告曝光 2590萬次
- 0.12 美金 => 廣告曝光 1970萬次 
再假設 DAU 是 MAU 的 1/10, 1/6, 1/2 的話
- 300萬
- 500萬
- 1000萬 
那用戶一天需看 3次 ~ 20 次廣告 
若用戶中午看一次,或是晚上在逛一次,或是一進站可以看到 2-3 則廣告,那單一用戶一天看個 6-10 個廣告也不會太誇張。以此數字來推論 DAU 應當落在 300萬-1000萬之間都還算合理。
單純靠 Google Adsense 其實也不會差太多。說不定改用插頁廣告,還有機會 eCPM 衝到 2-3 美金呢 XD

聊聊題外話,恰好昨晚逛 Alexa Top 100 網站找點子,意外看到一間美妝網站的流量從 50 闖進 30 名,應該是半年不到的時間。這個案例很令人感到驚奇,一間資本額不高的行銷公司(破百但沒千萬),在低卡可以看到一些聘人產文章的八卦故事,竟然能如此成長快速。

問幾位周邊未滿30的女同事都說沒在逛,但已經很容易在 FB 的廣告上被打中。最後跟強者大大細聊後,推論應當是...被高人收購了,以及目前的市場活動熱絡著:


果真是時也運也命也啊,只能處處把握機會!美妝聯盟一到位,美妝網站們就可以一起接收廣告流量啦。

只是同事打趣的說,最近口罩盛行,美妝上半年可能很辛苦。

註:盜版內容極有可能無法獲得 google adsense 廣告來源

2020年4月4日 星期六

再看一次「心之谷」/ 「側耳傾聽」



最近宮崎駿搬上了 NETFLIX ,就順手滑了一下 XD 明明昨晚在看「地獄」(瘟疫+WHO),今晚卻在看宮崎駿心之谷。

一開始只是想回顧片中經典的合唱曲,看著看著也把後半段也都追完了。令人回想起初中生活,當時引響最深的就是心之谷的這首歌。述說著自己當年的青澀與茫無目標的生活,想做什麼,卻什麼也沒頭緒。

此時此刻,時間貢獻給工作、家庭,近幾年提不起勁的,隨手的 side project 也了無新意,很適合回顧這動畫,想想、動動身,別再遲疑了。

2020年3月28日 星期六

[Python] 數據分析筆記 - 透過 pandas, scikit-learn 和 xgboost 分析 Kaggle airbnb-recruiting-new-user-bookings 案例

記得 2017 年也曾註冊 Kaggle 帳號,在上面挑個題目試試手氣,當時也有選中 Airbnb 來研究,可惜當年沒堅持下去,太多有趣的事了 XD 今年春天就來複習一下。

當初我是先從史丹佛 Andrew Ng 的課程看的,但大概只看個幾集就沒繼續,再過一陣子後就是台大教授林軒田的機器學習基石,我印象中有看完,因為我還在遲疑要不要接著看另一個進階課程,那時過境遷,沒再用都忘光了!
這些課程看著看著就不小心恍神了,接著自己僅用著一些原理去土砲...殊不知 Pandas 跟 scikit-learn 套件有多好用,當時只粗略用 Pandas 當 csv parser ,剩下的資料轉換、陣列計算還自己刻 numpy 架構去運算。所幸,終於有個更適合我這種懶人的微課程,那就是 Kaggle 的 Faster Data Science Education
我大概只需要從第三章 Intermediate Machine Learning 走完一遍就得到我想要的東西了。接著想找個戰場試試手氣,就又回想起 Kaggle 的 airbnb-recruiting-new-user-bookings 數據
接著用 airbnb-recruiting-new-user-bookings 關鍵字問個 google ,會發現到現在也有非常多人拿他當例子來分析,經典果真歷久不衰!大概不用破百行,就可以組出 airbnb 數據分析達八成的水準:

匯入函式庫:

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from xgboost import XGBClassifier

import numpy as np
import pandas as pd

import datetime


匯入 csv 檔案:

train_users = pd.read_csv('input/train_users_2.csv')

將 age 內容調整,包含去除輸入錯誤(太大或大小者),例如明顯輸入的是西元年,就順便幫轉一下:

data_checker = train_users.select_dtypes(include=['number']).copy()
data_checker = data_checker[ (data_checker.age > 1000) & (data_checker.age < 2010) ]
data_checker['age'] = 2015 - data_checker['age'] # 推論當年的資料,用 2015 年來相減對方不小心輸錯的出生年來得到年紀

for idx,row in data_checker.iterrows():
        train_users.at[idx,'age'] = row['age']

data_checker = train_users.select_dtypes(include=['number']).copy()
data_checker = data_checker[ (data_checker.age >= 2010) | (data_checker.age >= 100) | (data_checker.age < 13) ]
data_checker['age'] = np.nan
for idx,row in data_checker.iterrows():
        train_users.at[idx,'age'] = row['age']


處理時間欄位,轉成 datetime 型態,並轉成 weekday:

data_checker = train_users.loc[:, 'timestamp_first_active'].copy()
data_checker = pd.to_datetime( (data_checker // 1000000), format='%Y%m%d')
train_users['timestamp_first_active'] = data_checker

str_to_datetime_fields = ['date_account_created', 'date_first_booking']

for field in str_to_datetime_fields:
        train_users[field] = pd.to_datetime(train_users[field])

# to weekday

train_users['first_active_weekday'] = train_users['timestamp_first_active'].dt.dayofweek
for field in str_to_datetime_fields:
        train_users[field+'_weekday'] = train_users[field].dt.dayofweek

# remove datetime fields

train_users.drop(str_to_datetime_fields, axis=1, inplace=True)
train_users.drop(['timestamp_first_active'], axis=1, inplace=True)


處理 label 資料,主要轉成 one-hot encoding,並且去除一些數值,統一轉成 NaN:

categorical_features = [
        'affiliate_channel',
        'affiliate_provider',
        #'country_destination',
        'first_affiliate_tracked',
        'first_browser',
        'first_device_type',
        'gender',
        'language',
        'signup_app',
        'signup_method'
]
for categorical_feature in categorical_features:
        train_users[categorical_feature].replace('-unknown-', np.nan, inplace=True)
        train_users[categorical_feature].replace('NaN', np.nan, inplace=True)
        train_users[categorical_feature] = train_users[categorical_feature].astype('category')

# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.get_dummies.html
# Convert categorical variable into dummy/indicator variables.
train_users = pd.get_dummies(train_users, columns=categorical_features)


開始順練模型,建議先透過 sample 跑小量

# X = train_users.copy()
X = train_users.sample(n=3000,random_state=0).copy()
y = X['country_destination'].copy()
X = X.drop(['country_destination'], axis=1)

X_train, X_valid, y_train, y_valid = train_test_split(X, y)


print("Start to train...")

job_start = datetime.datetime.now()

my_model = XGBClassifier()
my_model.fit(X_train, y_train)

print("training done, time cost: ", (datetime.datetime.now() - job_start))

job_start = datetime.datetime.now()

predictions = my_model.predict(X_valid)
print("predict done, time cost: ", (datetime.datetime.now() - job_start))

print("score:", accuracy_score(predictions, y_valid))


運行結果:

Start to train...
training done, time cost:  0:00:14.270242
predict done, time cost:  0:00:00.056500
score: 0.8466666666666667


沒想到只需做一些處理,運算玩就有八成準確率了!以上還沒使用 sessions 資料。完整程式碼請參考:github.com/changyy/study-kaggle-airbnb-recruiting-new-user-bookings

2020年3月22日 星期日

[GoogleSheet] 透過 Query 取出動態欄位的極限值,以 GOOGLEFINANCE 為例

GoogleSheet, =QUERY(GOOGLEFINANCE("NASDAQ:ZM", "low","1/1/2020",365,"DAILY"),"Select Min(Col2) label Min(Col2)''",1)

故事是這樣的,目前透過 GoogleSheet 維護一些資訊,有些服務可以立即幫你長很多筆資料,但有時只需要其中一項極值資訊。

例如 GOOGLEFINANCE - https://support.google.com/docs/answer/3093281 可以一口氣列出幾定天數的數據,如:

=GOOGLEFINANCE("NASDAQ:ZM", "low","1/1/2020",365,"DAILY")

若只想看極值,就可以再透過 Query - https://support.google.com/docs/answer/3093343 處理

最低極值:

=QUERY(GOOGLEFINANCE("NASDAQ:ZM", "low","1/1/2020",365,"DAILY"),"Select Min(Col2) label Min(Col2)''",1)

最高極值:

=QUERY(GOOGLEFINANCE("NASDAQ:ZM", "high","1/1/2020",365,"DAILY"),"Select Max(Col2) label Max(Col2)''",1)

收工!真是美好的一天

2020年3月21日 星期六

[C] 嵌入式 Web Server + cgi-bin + JSON = thttpd + cgic + cJSON 開發筆記 @ macOS

記得七八年前,是在嵌入式平台上寫 C++ 的 CGI 練習,現在則是在試著寫 C 語言。以下幾個套件還滿夠用的,筆記一下:
而 cgic 跟 cJSON 都是很精簡的單一檔案,非常方便,建議直接看他們的 header file ,就可以很快了解其概念,其中 cJSON 適合看 README ,裡頭提到了需要面對的記憶體管理。

以下是 CMake 編譯範例:

# https://raw.githubusercontent.com/boutell/cgic/master/cgic.h
# https://raw.githubusercontent.com/boutell/cgic/master/cgic.c
set(CGIC_VERSION "cgic-2.07")

include_directories(deps/${CGIC_VERSION}/include)
add_library(cgic
        deps/${CGIC_VERSION}/src/cgic.c
)

# https://raw.githubusercontent.com/DaveGamble/cJSON/v1.7.12/cJSON.h
# https://raw.githubusercontent.com/DaveGamble/cJSON/v1.7.12/cJSON.c
set(CJSON_VERSION "cjson-1.7.12")
include_directories(deps/${CJSON_VERSION}/include)
add_library(cjson
deps/${CJSON_VERSION}/src/cJSON.c
)


如此,後續要編譯時,就可以:

add_executable(study-app.cgi
        src/study-app.c
)
target_link_libraries(study-app.cgi
        cgic
        cjson
        curl
)


相關筆記在此: changyy/thttpd-cgi-study/blob/master/src/study-app.c

2020年3月17日 星期二

使用 curl 仿 DLNA controller 指令播放影片: DLNA protocol + SOAP protocol

大概去年就拿 DLNA 當做個小題目研究了一下,最近工作又需要,再度翻出來複習一下。使用了別人寫的 python 小工具,非常小巧精美:github.com/cherezov/dlnap

裝置搜尋:

$ python dlnap.py
No compatible devices found.
$ python dlnap.py
Discovered devices:
 [a] Name1 @ 192.168.1.2
 [a] Name2 @ 192.168.1.3


播放影片:

$ python dlnap.py -d Name1 --play http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
No compatible devices found.

$ python dlnap.py -d Name1 --play http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
Name1 @ 192.168.1.2
POST AVTransport/control HTTP/1.1
User-Agent: dlnap.py/0.15
Accept: */*
Content-Type: text/xml; charset="utf-8"
HOST: 192.168.1.2:60099
Content-Length: 558
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"
Connection: close

<?xml version="1.0" encoding="utf-8"?>
         <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <s:Body>
               <u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
                  <InstanceID>0</InstanceID><CurrentURIMetaData></CurrentURIMetaData><CurrentURI>http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4</CurrentURI>
               </u:SetAVTransportURI>
            </s:Body>
         </s:Envelope>
POST AVTransport/control HTTP/1.1
User-Agent: dlnap.py/0.15
Accept: */*
Content-Type: text/xml; charset="utf-8"
HOST: 192.168.1.2:60099
Content-Length: 401
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Play"
Connection: close

<?xml version="1.0" encoding="utf-8"?>
         <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <s:Body>
               <u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
                  <InstanceID>0</InstanceID><Speed>1</Speed>
               </u:Play>
            </s:Body>
         </s:Envelope>


可惜的,在測試播放影片時總不太順利,推論應當是兩個指令太快,改用 curl 指令測試就很正常:

1. 先設定影片

$ cat SetAVTransportURI.record
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
<InstanceID>0</InstanceID>
<CurrentURIMetaData></CurrentURIMetaData>
<CurrentURI>http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4</CurrentURI>
</u:SetAVTransportURI>
</s:Body>
</s:Envelope>

$ curl -v -X POST --data @SetAVTransportURI.record  -H 'SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"' -H 'Content-Type: text/xml; charset="utf-8"' http://192.168.1.2:60099/AVTransport/control
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 192.168.1.2:60099...
* TCP_NODELAY set
* Connected to 192.168.1.2 (192.168.1.2) port 60099 (#0)
> POST /AVTransport/control HTTP/1.1
> Host: 192.168.1.2:60099
> User-Agent: curl/7.68.0
> Accept: */*
> SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"
> Content-Type: text/xml; charset="utf-8"
> Content-Length: 476
>
* upload completely sent off: 476 out of 476 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< EXT:
< CONTENT-TYPE: text/xml; charset="utf-8"
< SERVER: POSIX, UPnP/1.0, Intel MicroStack/1.0.2777
< Content-Length: 336
<
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <u:SetAVTransportURIResponse xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">

      </u:SetAVTransportURIResponse>
   </s:Body>
* Connection #0 to host 192.168.1.2 left intact
</s:Envelope>


2. 呼叫播放

$ cat Play.record
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
<InstanceID>0</InstanceID>
<Speed>1</Speed>
</u:Play>
</s:Body>
</s:Envelope>

$ curl -v -X POST --data @Play.record  -H 'SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Play"' -H 'Content-Type: text/xml; charset="utf-8"' http://192.168.1.2:60099/AVTransport/control
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 192.168.1.2:60099...
* TCP_NODELAY set
* Connected to 192.168.1.2 (192.168.1.2) port 60099 (#0)
> POST /AVTransport/control HTTP/1.1
> Host: 192.168.1.2:60099
> User-Agent: curl/7.68.0
> Accept: */*
> SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Play"
> Content-Type: text/xml; charset="utf-8"
> Content-Length: 316
>
* upload completely sent off: 316 out of 316 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< EXT:
< CONTENT-TYPE: text/xml; charset="utf-8"
< SERVER: POSIX, UPnP/1.0, Intel MicroStack/1.0.2777
< Content-Length: 310
<
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <u:PlayResponse xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">

      </u:PlayResponse>
   </s:Body>
* Connection #0 to host 192.168.1.2 left intact
</s:Envelope>

2020年3月15日 星期日

Xbox Project xCloud / Game Streaming 設定教學筆記 @ Xbox One S / Android 9 / 小米A3

Xbox xCloud game streaming

幾年前買了一台 Xbox One S 當藍光播放器,今年起,因為武漢肺炎等防疫關係,開始體驗 Xbox 商城。買了幾款遊戲試試,試了才發現 Xbox 真的發展的不錯!雖說遊戲方面推論還是會輸 PS ,但 Xbox 的整個模式已經漸漸成形,包括遊戲商城、包月100款遊戲(Xbox Game Pass)、包月每月兩款免費遊戲(Xbox Live Gold)等等,當然更免不了不時來一下遊戲特價,不斷地推坑 XD

原先是想多找哪邊有折扣,找著找著在 Xbox 台灣 FB 粉絲團看到新訊:
Xbox Project xCloud 雲端串流服務
原來在 2018 年 10 月就曾宣布 xCloud 了,本意是把主機雲端化,玩遊戲則是雲台運算並把結果 streaming 到手機上。而想要來體驗 Project xCloud (Preview) 時,發現現況也有 "主機串流" 服務,可以嘗試把 Xbox 輸出直播出去,這恰好打中我的需求:
  • 家中只有一台電視
  • 電視不能一直玩遊戲 / 有些暴力遊戲不宜給小孩看
這時,除了把 Xbox One HDMI out 輸出到另一台螢幕、投影機外,並沒有其他高招,大概就剩 HDMI WIFI 傳輸了!結果這時看到 Android app 已經可以來串流了,實在美好!

整個設定過程:
1. 將 Xbox One S 加入 Xbox One Preview Update
- Microsoft Store 下載 Xbox Insider 中心
- 加入 Xbox 測試人員計畫 https://beta.support.xbox.com/help/account-profile/manage-account/xbox-insider-program
- 安裝 Xbox One 更新預覽 版 (在系統區)
2. 設定主機串流
- 設定 -> 裝置與串流 -> 主機串流 -> 測試主機串流
- 可以看到要求更新控制器,以及電源選項要更新 
3. 更新藍芽控制器韌體
- 設定 -> 裝置與串流 -> 配件 -> 選擇 Xbox 無線控制器 -> 下方 ... 可以更新韌體 
4. 電源設定
- 設定 -> 一般 -> 電源模式與串流 -> 電源模式: 即時啟動
Xbox xCloud game streaming setup test

如此,再回去設定主機串流,測試主機串流,當測試通過才可以啟用!如果被網路上傳速度擋下,剛好手機有4G吃到飽,可以試看看把 Xbox 改用手機共享網路先設法通過測試,通過後再把 Xbox 網路改回來。

接著在 Google play 搜尋 xbox game streaming ,就能找到 Xbox Game Streaming (Preview) ,安裝完後,很快就能設定完成。主要把 Xbox 無線控制器跟手機藍芽配對後,就可以正常遠端 Xbox One S 了,嘗試把玩 NBA 2K20 或是快打旋風30周年慶,對於這種需要高更新畫面的遊戲,玩幾下就會看到極限了 XD 倒是老人回憶之旅那種篇益智遊戲,則非常的舒服。

Xbox xCloud Android Setup

Xbox xCloud Android Connect

如此就能體會主機串流的功能了!而大部分的體驗流程都是先到 google play 下載 xbox game streaming 後,接著得到你所在的區域並不支援 XD 殊不知要先從 Xbox One 更新預覽版才能開啟!

2020年3月13日 星期五

[macOS] GnuPG 筆記 - key generator / import / export / public key / private key

最近在跟大公司討論 DRM 事宜,要求信件採用 PGP 加密保護。筆記一下環境建置:

GnuPG 安裝:
  • 從 https://gnupg.org/download/ 到 GnuPG for OS X 區
  • 下載 GnuPG-2.2.19.dmg ,安裝後在 /usr/local/gnupg-2.2/
  • 推論環境變數可能就能找到 gpg2 ,或是用 /usr/local/gnupg-2.2/bin/gpg2
產生 GPG Keys(依照對方的使用設定):

$ gpg2 --full-generate-key
gpg (GnuPG) 2.2.19; Copyright (C) 2019 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
  (14) Existing key from card
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: HelloWorld
Email address: [email protected]
Comment:
You selected this USER-ID:
    "HelloWorld <[email protected]>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.


Passphrase: HelloWorld!!


We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key ####### marked as ultimately trusted
gpg: directory '/path/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/.gnupg/openpgp-revocs.d/#1#2#3#4#5#6#7#8#9#0.rev'
public and secret key created and signed.

pub   rsa2048 2020-03-13 [SC]
      #1#2#3#4#5#6#7#8#9#0
uid                      HelloWorld <[email protected]>
sub   rsa2048 2020-03-13 [E]


匯出 Public Key 靠這招:

$ gpg2 --armor --export "HelloWorld <[email protected]>" > your-pubkey.asc

$ gpg2 --armor --export "#1#2#3#4#5#6#7#8#9#0" > your-pubkey.asc


匯出 private key:

$ gpg2 --export-secret-keys "HelloWorld <[email protected]>" > your-private-key.asc

$ gpg2 --export-secret-keys "#1#2#3#4#5#6#7#8#9#0" > your-private-key.asc


列出目前的 keys:

$ gpg2 --list-keys
/path/.gnupg/pubring.kbx
------------------------------------
pub   rsa2048 2020-03-13 [SC]
      #1#2#3#4#5#6#7#8#9#0
uid           [ unknown] HelloWorld <[email protected]>
sub   rsa2048 2020-03-13 [E]

$ gpg2 --list-secret-keys
/path/.gnupg/pubring.kbx
------------------------------------
sec   rsa2048 2020-03-13 [SC]
      #1#2#3#4#5#6#7#8#9#0
uid           [unknown] HelloWorld <[email protected]>
ssb   rsa2048 2020-03-13 [E]


刪除 Keys,若該 key 組合內有 secret key ,需要先刪除 secret key  :

$ gpg2 --delete-secret-keys "#1#2#3#4#5#6#7#8#9#0"

刪除 public key:

$ gpg2 --delete-keys "#1#2#3#4#5#6#7#8#9#0"

匯入 Keys:

$ gpg2 --import your-private-key.asc
$ gpg2 --import your-pubkey.asc


使用 Keys 做加密,產生 *.gpg 檔案:

$ cat /tmp/text
hello world
$ gpg2 -r "#1#2#3#4#5#6#7#8#9#0" -e /tmp/text
$ ls /tmp/text.gpg
/tmp/text.gpg


使用 Keys 對 *.gpg 解密,若沒有 private key 獲得到:

$ gpg2 -r "#1#2#3#4#5#6#7#8#9#0" -d /tmp/text.gpg
gpg: encrypted with 2048-bit RSA key, ID ######, created 2020-03-13
      "HelloWorld <[email protected]>"
gpg: decryption failed: No secret key


有 private key 就會正常解出來:

$ gpg2 -r "#1#2#3#4#5#6#7#8#9#0" -d /tmp/text.gpg
gpg: encrypted with 2048-bit RSA key, ID #, created 2020-03-13
      "HelloWorld <[email protected]>"
hello world

2020年2月27日 星期四

kobo desktop 下載 , kobo app download @ macOS / Windows

昨晚跟在 FB 工作的學弟通個電話,聊了一陣子,被推坑看了一本 2017 年再版的書。找了一陣子,實體書都賣光了,只剩 KOBO 有電子書在賣,恰好當年 Kobo 在台灣推廣時,一直說第一次購書可以折 100 元,當時 NETFLIX 給力很夯,只是我最後買了實體書 XD 因此,那一百元折價一直沒用,就這樣很快地刷卡。

接著下一刻就囧了,大家都說可以用電腦看,找了很久台灣官網上沒有下載連結,輾轉得知改用美版網站就有了!
  • https://www.kobo.com/us/en/p/desktop 下方有 Download Now
    • https://kbdownload1-a.akamaihd.net/desktop/kobodesktop/kobosetup.dmg
    • https://kbdownload1-a.akamaihd.net/desktop/kobodesktop/kobosetup.exe
最近共用了 Kindle 看書、豆瓣閱讀 Mobile app 跟 Web 觀看付費書籍,接下來試試 Kobo 囉

2020年2月19日 星期三

[Linux] 在 Ubuntu 重編 git 處理 error: gnutls_handshake() failed: A TLS fatal alert has been received @ Ubuntu 12.04

當使用 git clone https:// 來源時,會出現以下錯誤訊息:

error: gnutls_handshake() failed: A TLS fatal alert has been received. while accessing https://service/project.git/info/refs
fatal: HTTP request failed

解法就是重編 git (細節似乎是 gnutls 問題)

流程:
  1. 到 https://github.com/git/git/releases 下載 git 程式碼(此例用 v2.25.1)
  2. 安裝 libcurl4-openssl-dev libexpat1-dev 等編譯環境
  3. 編他:./configure --with-expat --with-curl --with-openssl
連續動作:

$ dpkg -l | grep gnutls
ii  libcurl3-gnutls                             7.22.0-3ubuntu4.17                      Multi-protocol file transfer library (GnuTLS)
ii  libgnutls-dev                               2.12.14-5ubuntu3.14                     GNU TLS library - development files
ii  libgnutls-openssl27                         2.12.14-5ubuntu3.14                     GNU TLS library - OpenSSL wrapper
ii  libgnutls26                                 2.12.14-5ubuntu3.14                     GNU TLS library - runtime library
ii  libgnutls26:i386                            2.12.14-5ubuntu3.14                     GNU TLS library - runtime library
ii  libgnutlsxx27                               2.12.14-5ubuntu3.14                     GNU TLS library - C++ runtime library
ii  libneon27-gnutls                            0.29.6-1ubuntu1                         HTTP and WebDAV client library (GnuTLS enabled)
$ sudo apt-get install libcurl4-openssl-dev tcl-dev libexpat1-dev
$ wget https://github.com/git/git/archive/v2.25.1.tar.gz
$ tar -xvf v2.25.1.tar.gz && cd git-v2.25.1
$ make configure
$ ./configure --with-expat --with-curl --with-openssl
$ make test
$ sudo make install


其中 make test 會跑一陣子,可以選擇執行 XD

2020年2月8日 星期六

[macOS] 替 Macbook Pro 2015 13寸 升級為 1TB SSD / MacBook Pro (Retina, 13-inch, Early 2015) / MacBookPro12,1

MacbookProUpgradeSSD01

前陣子 幫 Macbook Pro 更換膨脹電池 時,意外發現這台 2015 年的 Macbook Pro 可以自行換 SSD,瞬間心花怒放 XD 實在是當年花了不少錢買了 256GB 的 Macbook Pro ,接著近半年想要編譯 Chromium / CEF 時,常常移來移去就是為了多生出個 40GB,更別說系統槽光 Xcode 要更新時必須砍掉 Xcode 重新安裝才行,不能當下直接更新成新版。就這樣,評估一下大家換 SSD 的心得就衝了。

首先 Apple SSD 是客製化的接頭,需要買一張轉卡。其次則是要買大廠 SSD 才不會踩到 macOS 深度睡眠的問題。於是乎就賭了一下,在蝦皮買了 250 元左右的轉卡(網路上也有人賣到 500),接著採購 INTEL 660p 1TB 的 SSD ,真是奢侈的空間啊!就這樣大概 4000 左右就完成更新了!而選擇買兩百多的轉卡,是因為覺得上頭沒有 IC?應當只是接頭轉換,再加上很多討論文就說買兩百多即可。而買 INTEL 660p 1TB (讀1800M/寫1800M) 而不買金士頓 A2000 1TB (讀2200M/寫2000M) 是覺得筆電已經五年前了,應當讀寫效率沒這麼好不需多追求多好,反正再過幾年說不定又換了筆電了?

而經歷過拆電池的洗禮,拆裝 SSD 變得十分簡單,反而出現的兩段插曲!

MacbookProUpgradeSSD03

MacbookProUpgradeSSD04

一則是 macOS USB 安裝碟出包,用去年做的 macOS USB 安裝碟開機會看到 macOS 多國語當機畫面,當下還以為硬體不合。後來換了一個 USB 重新製作就搞定

另一個則是 iCloud 同步問題,用了新硬碟後立刻重灌完畢,重灌後發現 "桌面與文件檔案夾" 並沒有自動同步,就是連目錄、檔案都沒看到任何蹤影,且連續兩天沒解決。試了很多怪招,像是回到舊硬碟關掉 iCloud、或是網路上常見的刪除 iCloud 帳號資料重新開機等等。後來覺得照片和 Safari 書籤/密碼都有同步,那就擺在個幾晚,果真第三天起床後就看到了!估計是 iCloud 的桌面被我擺太多東西,所以要花很多時間初始化同步環境。

當身邊就多了一隻 256GB SSD 時,覺得晾在很浪費,躊躇著到底要不要花錢買專用的外接盒,研究完想了兩天,牙一咬就下單了,後來得知連貴鬆鬆的 OWC-MAC Envoy Pro 外接盒不一定支援,例如 Apple 原廠 SSD 若是 Toshiba 就不行,有需要的話,建議可以先多問一下賣家。

MacbookProUpgradeSSD02

MacbookProUpgradeSSD05

看來年初繳的3C稅非常恐怖:
  • 轉卡含運: 250元
  • Intel 660p 1TB: 3900元
  • OWC-MAC Envoy Pro 外接盒: 3300元
再加上處理膨脹的電池問題,不多不少就剛好破萬 Orz

除了轉卡只能在蝦皮等非官方地方買外,其餘的則盡可能在知名網路購物平台購買,主要是東西很貴,不小心出問題很麻煩。

而除了 OWC 外,創建也有出一堆 Macbook SSD 升級包,像是升級包內的 SSD 不用轉卡,直接是 Apple 的形狀(?),但...真的貴不少!大概貴一倍吧,像是升級包 7000 元(含 SSD + 外接盒) 只能擁有 240GB 的空間,若是 1TB 可是要價 1萬五了! 可參考 JetDrive 855 關鍵字或是 PCHOME Apple專用★SSD ,真是商機無限啊。

最後 又逢全球疫情 "遠端工作" 的時機興起,剛好擴充完的筆電環境可以無憂無慮在家工作了 :P 

2020年2月1日 星期六

[macOS] 替 Macbook Pro 2015 13寸 換電池 / MacBook Pro (Retina, 13-inch, Early 2015) / MacBookPro12,1 / A1582

Retina, 13-inch, early 2015 換電池 A1582

我也忘了多久,可能有半年以上,筆電一直蓋不緊。昨晚不知哪根筋不對,仔細觀摩了一下,推論應當是電池膨脹,沒多久隨意查到 A1582 這個電池型號,就在 PCHOME 24小時下單,雖說 PCHOME 24 標記在 "副廠-其他牌子" 有點抖,還是下手了。果真拿到貨看了一下,有些線的外觀有點兒粗糙,推論應當還不影響什麼,至少裝完從系統資訊可以看到:

  電量資訊:
  剩餘電量(mAh): 6515
  已充飽電:
  正在充電:
  總充電容量(mAh): 6581
  健康狀態資訊:
  循環使用次數: 0
  狀態: 正常
而拆電池的心得:
需要勇氣跟一支尺!因為電池是被黏著的,要拆必須想辦法扳起來,尺是個好幫手。
購買來的電池,通常都會附上特殊的五跟六螺絲,用來拆背蓋以及裡頭的螺絲。

Retina, 13-inch, early 2015 換電池 A1582

個人龜毛了點,覺得不美觀的地方:

Retina, 13-inch, early 2015 換電池 A1582

接著,拆完筆電背蓋,可以看到膨脹的景觀:

Retina, 13-inch, early 2015 換電池 A1582

開始動工,先把電池連接線拔起來:

Retina, 13-inch, early 2015 換電池 A1582

再把中間的連接線處的螺絲蓋保護移除,並把中間接線拔掉:

Retina, 13-inch, early 2015 換電池 A1582

最後則是右邊還有個螺絲要移除:

Retina, 13-inch, early 2015 換電池 A1582

接著就努力把電池拔起來吧!我是先靠著膨脹的電池空隙,先把左右兩邊的電池靠著尺插入後,左右旋轉尺來小幅度處理:

Retina, 13-inch, early 2015 換電池 A1582

Retina, 13-inch, early 2015 換電池 A1582

拆到只剩中間,就真的比較難拆,有那種要很使勁的那種勇氣,因為中間黏的面積也大:

Retina, 13-inch, early 2015 換電池 A1582

Retina, 13-inch, early 2015 換電池 A1582

接著,就要嘗試連接新的電池了!由於賣家都說,建議驗證完電池沒問題再粘上去,但實務上新的電池有透明板子在保護,其實無法先擺入再看看,我最後是先讓電池稍微在那個位置處連上連接好,再用筆電背蓋護一下來開機驗身,驗完立馬關機,轉過來,在拔掉電池連接線。

Retina, 13-inch, early 2015 換電池 A1582

Retina, 13-inch, early 2015 換電池 A1582

如此,就可以撤掉新電池的透明背板等保護,喬好位置後再讓他好好黏好:

Retina, 13-inch, early 2015 換電池 A1582

收工!可以把舊電池回收了

Retina, 13-inch, early 2015 換電池 A1582