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

2020年1月21日 星期二

[開箱] EZCast Ultra 與 Chromecast Ultra 比較

EZCast Ultra 01

不知為何手邊多了這產品可以體驗一下。大概是年節將近,不知該帶什麼伴手禮 (誤)

EZCast Ultra 02

包裝盒上有六國語言,有繁體中文!

EZCast Ultra 03

內容物有 EZCast Ultra 主體一個、一條 USB <-> Typc-C 線(供電)、一條 HDMI 公對公。因此 EZCast Ultra 已經跟主流產品一樣,採用 Type-C 接頭供電了。對比三年前在 2016 年底出產的 Chromecast Ultra 還在採用 Micro-USB ,也算是一時之選吧?

EZCast Ultra 04

開機畫面還滿精美的,這灣好眼熟,不曉得是不是義大利的?

EZCast Ultra 05

接著下載 EZCast app ,掃螢幕上的 QRCode 有引導設定,還可以透過藍芽設定 EZCast Ultra 對外網路,滿方便的。

EZCast Ultra 06

最後,提一下跟 Chromecast Ultra 主要差別,因為我剛好是 Chromecast 重度使用者 XD

EZCas tUltra and Chromecast Ultra

  • 外觀上不會差太多,Chromecast Ultra 用 Micro-USB 接頭供電;EZCast Ultra 用 Type-C 接頭供電
  • 兩者都支援 4K 輸出
  • Chromecast Ultra 要求一定要有對外網路的使用環境;EZCast Ultra 沒有對外網路時,一樣可以使用,如透過 DLNA 協定,把區網內的 NAS 拉影片來播放
  • Chromecast Ultra 有支援 DRM,看 Netflix 必備的環境;EZCast Ultra 支援 MIRCAST/AirPlay/DLNA 常見標準協定,支援很多作業系統,若沒有在觀看 DRM 保護的數位影音,應當都還非常方便
  • Chromecast 閒置時可以播放個人照片;EZCast Ultra 則是有動態風景桌布可看
  • Chromecast 跟 EZCast Ultra 都有支援音控服務,但 Chromecast 是天生跟 Google 生態完美整合,如 Android / Chrome 都可方便投放,像常用 Spotify 就可以很方便的叫 Chromecast 或 Google Home mini 播放音樂
EZCast Ultra 有其跨 OS 優勢跟特色,補足 Chromecast Ultra 先天設計的限制,都是很不錯的產品,希望哪天 EZCast Ultra 可以補齊 DRM 就完美了!

據說很多公司想跟 NETFLIX 談 DRM 都不是很順利,大多要求 Android-based 類的產品,如何能取得 NETFLIX 公司信任,真是一門學問。