搜尋此網誌

2015-12-30

多平台的效能測試套件 - Phoronix Test Suite

Phoronix Test Suite

先以 root 權限安裝依賴套件。
apt-get install php-cli php-gd php-xml
下載後手動安裝 phoronix-test-suite。
dpkg -i phoronix-test-suite*.deb
第一次執行需要填一些使用同意書或是否自動匿名上傳測試結果。
phoronix-test-suite
列出支援的測試項目。
phoronix-test-suite list-tests
安裝 pts/x264;如果有缺依賴的套件,則需要 root 權限來自動安裝;沒安裝成功的話,通常是套件的版本不符合,可以到 ~/.phoronix-test-suite/installed-tests/ 找一下套件的 install.log,嘗試去修復它,通常是可以從 testing 或 backport 安裝較新的套件來解決。
phoronix-test-suite install pts/x264
測試 pts/x264。
phoronix-test-suite run pts/x264
收工。

2015-11-24

合併與分割 tiff 檔案

LibTIFF

基本上是要用 libtiff-tools 的 tiffcp 做合併,用 libtiff 的 tiffsplit 做分割。

以 root 權限安裝。
apt-get install libtiff-tools
合併 t 開頭的 tif 檔為 output.tif。
tiffcp t*.tif output.tif
分割 output.tif 為 s 開頭的一連串 tiff 檔。
tiffsplit output.tif s
把 lexical 順序轉為數字順序。
find -name 's*.tif' | gawk 'BEGIN{a=1}{printf "mv %s s%03d.tif\n", $0, a++}' | bash
收工。

2015-11-23

將 vobsub 轉為 srt - 使用 tesseract-ocr 與 OGMRip

OGMRip

有鑑於 subtitleripper 已經很久沒更新了,目前活躍的替代品是 OGMRip,因此改寫舊文

其實 OGMRip 是一套完整的 DVD 工具,而且附有 GUI,不過個人認為比不上 HandBrake,所以只著重在字幕工具上;與 subtitleripper 一樣,字幕工具都是從 mplayer 承襲而來的。

以 root 權限安裝。
apt-get install ogmrip
將 sample.idx 與 sample.sub 轉為 t 開頭的圖片與 t.xml。
subp2tiff sample -o t
轉出的圖片只有含文字的部份且已經是 tiff 格式,因此無需裁切就可以進行 OCR。
find -name "*.tif" -exec tesseract {} {} -l chi_tra -psm 6 \;
將便是完成的檔案合併成 sample.srt。
subptools -s -t srt -i t.xml -o sample.srt
兩個工具其實互有長短,subtitleripper 的 vobsub2pgm 可以設定轉出文字的字型的陰影、字型的內部、字型的外框、背景的底色,但是圖片檔是全尺寸的,所以需要裁切;OGMRip 的 subp2tif 則是可以直接丟進去 OCR,但卻無法自行設定字型的陰影、字型的內部、字型的外框、背景的底色,轉出的圖片檔有淺灰色的字型的外框,不過在 tesseract-ocr 辨識沒有顯著的差異。

整體來說,選擇還活著的 project 還是比較有保障。

2015-11-22

低 CPU 使用率的快速壓縮 - LZ4

LZ4 不算是什麼新的東西,不過它在低 CPU 使用率與速度上令人印象深刻。

先把 9.3GB 的 XBRL 打包成 tar。
tar cf xbrl.tar *.xml
測量 7z 最高壓縮的時間。
time 7z a xbrl.tar -mx=9 xbrl.7z
測量 pigz 最高壓縮的時間,使用四核心 CPU。
time pigz -k xbrl.tar
測量 LZ4 最快壓縮的時間。
time lz4 xbrl.tar
測量 LZ4 最高壓縮的時間。
time lz4 -9 xbrl.tar
壓縮成果依序是 265MB、955MB、1.5GB、1.1GB,時間依序是 42m39s、1m32s、1m38s、5m18s,LZ4 在最高壓縮表現不出色,但在最快壓縮上非常突出,尤其是相較於 pigz 是多核心的平行壓縮。

2015-09-22

golang 回傳 UTF8 的內碼(code point)

由於 golang 的 string 全部採用 UTF8,代表最基本的 CJK 字元(word rune)是由四個基本單位(rune)組成,所以回傳的格式上跟一般有點不同。
package main

import (
    "fmt"
)

func main() {
    fmt.Printf("%#U\n", '日')
}
執行後回傳。
U+65E5 '日'
結束。

Reference: Strings, bytes, runes and characters in Go

2015-09-19

直接抽取 Excel BIFF8 中的值 - golang

由於某些機構仍然使用 Excel 發布資料,因此直接抽取某些欄位是必要的,目前應該是數值與文字就夠了。

Excel 的內部格式以 Sheets 來劃分,第一個 Sheets 放一些全域的資料,譬如文字,而接下來的 Sheets 依序為 Sheet1、Sheet2、Sheet3 等。Excel 的編碼為 Little Endian,文字編碼採用 UTF16,所以需要解碼為 UTF8 才能讓 golang 使用。

程式碼
package main

import (
    "fmt"
    "io/ioutil"
    "bytes"
    "encoding/binary"
    "golang.org/x/text/encoding/unicode"
    "golang.org/x/text/transform"
)

type BIFF8 struct {
    Sheets [][]byte
    SST []string
}

func RKValue(rk []byte) float64 {
    var value float64
    var rk_int32 int32
    rk_float64 := make([]byte, 8)

    if ((rk[0] & 0x02) == 0x02) {
        /* bit 1 == 1, signed integer */
        binary.Read(bytes.NewBuffer(rk), binary.LittleEndian, &rk_int32)
        value = float64(rk_int32 >> 2)
    } else {
        /* bit 1 == 0, IEEE 64-bits format */
        copy(rk_float64[5:], rk[1:])
        rk_float64[4] = rk[0] >> 2
        binary.Read(bytes.NewBuffer(rk_float64), binary.LittleEndian, &value)
    }

    /* divide by 100 */
    if ((rk[0] & 0x01) == 0x01) {
        value /= 100
    }

    return value
}

func Float64Value(number []byte) float64 {
    var value float64
    binary.Read(bytes.NewBuffer(number), binary.LittleEndian, &value)

    return value
}

/* modified from bytes.Count */
/* Get sheets records between BOFs and EOFs */
func (biff *BIFF8) NewSheetsRecords(s []byte) {
    /* BOF = 0809h */
    sep := []byte{0x09, 0x08, 0x10, 0x00, 0x00, 0x06}
    n := len(sep)
    if n == 0 || n > len(s) {
        biff.Sheets = [][]byte{}
    }
    temp := 0
    ret := [][]byte{}
    c := sep[0]
    i := 0
    t := s[:len(s)-n+1]
    for i < len(t) {
        if t[i] != c {
            o := bytes.IndexByte(t[i:], c)
            if o < 0 {
                break
            }
            i += o
        }
        if n == 1 || bytes.Equal(s[i:i+n], sep) {
            if temp > 0 {
                ret = append(ret, s[temp:i])
            }
            temp = i
            i += n
            continue
        }
        i++
    }
    ret = append(ret, s[temp:])
    i = bytes.Index(ret[len(ret)-1], []byte{0x0A, 0x00, 0x00, 0x00})
    ret[len(ret)-1] = ret[len(ret)-1][:i+4]
    biff.Sheets = ret
}

func (biff *BIFF8) NValue(sheet, row, col uint16) float64 {
    bs := biff.Sheets[sheet]
    var i uint = 0
    var length uint = uint(len(bs))
    var value float64
    for i < length {
        if bytes.Equal(bs[i:i+2], []byte{0x7E, 0x02}) && binary.LittleEndian.Uint16(bs[i+4:i+6]) == row && binary.LittleEndian.Uint16(bs[i+6:i+8]) == col {
            value = RKValue(bs[i+10:i+14])
        } else if bytes.Equal(bs[i:i+2], []byte{0x03, 0x02}) && binary.LittleEndian.Uint16(bs[i+4:i+6]) == row && binary.LittleEndian.Uint16(bs[i+6:i+8]) == col {
            value = Float64Value(bs[i+10:i+18])
        }
        i = i + uint(binary.LittleEndian.Uint16(bs[i+2:i+4]) + 4)
    }
    return value
}

func (biff *BIFF8) ParseSST() {
    var length uint16
    var values []string
    bs := biff.Sheets[0]
    i := bytes.Index(bs, []byte{0xFC, 0x00})
    /* SST = FCh */
    if bytes.Equal(bs[i:i+12], []byte{0xFC, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) == true {
        biff.SST = values
        return
    }
    binary.Read(bytes.NewBuffer(bs[i+2:i+4]), binary.LittleEndian, &length)
    /* rgb of Shared String Table */
    j := i + 12
    for j < (i + 4 + int(length)) {
        var cch, crun uint16
        var grbit uint8 = bs[j+2]
        var cchExtRst uint32 = 0
        var shift int = 3
        binary.Read(bytes.NewBuffer(bs[j:j+2]), binary.LittleEndian, &cch)
        if grbit & 0x04 != 0 {
            shift += 4
            binary.Read(bytes.NewBuffer(bs[j+3:j+7]), binary.LittleEndian, &cchExtRst)
        }
        if grbit & 0x08 != 0 {
            shift += 2
            binary.Read(bytes.NewBuffer(bs[j+3:j+5]), binary.LittleEndian, &crun)
            binary.Read(bytes.NewBuffer(bs[j+5:j+9]), binary.LittleEndian, &cchExtRst)
        }
        if grbit & 0x01 != 0 {
            cch *= 2
            bs_UTF8, _, _ := transform.Bytes(unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder(), bs[j+shift:j+shift+int(cch)])
            values = append(values, string(bs_UTF8))
        } else {
            values = append(values, string(bs[j+shift:j+shift+int(cch)]))
        }
        j = j + shift + int(cch) + int(crun) * 4 + int(cchExtRst)
    }

    biff.SST = values
}

func (biff *BIFF8) SValue(sheet, row, col uint16) string {
    var isst uint32
    bs := biff.Sheets[sheet]
    buf := new(bytes.Buffer)
    binary.Write(buf, binary.LittleEndian, row)
    binary.Write(buf, binary.LittleEndian, col)
    pattern := []byte{0xFD, 0x00, 0x0A, 0x00}
    pattern = append(pattern, buf.Bytes()...)
    i := bytes.Index(bs, pattern)
    binary.Read(bytes.NewBuffer(bs[i+10:i+14]), binary.LittleEndian, &isst)

    return biff.SST[isst]
}

/* BIFF8 only */
func main() {
    bs, _ := ioutil.ReadFile("sample.xls")
    var xls BIFF8
    xls.NewSheetsRecords(bs)
    xls.ParseSST()
    fmt.Println(xls.NValue(1,4,3))
    fmt.Println(xls.SValue(1,9,1))
}
選擇 sample.xls 並抽取 Sheet1 中 D5 的數值與 B10 的文字。

Reference:
BiffView Tool
MICROSOFT OFFICE EXCEL 97-2007 BINARY FILE FORMAT SPECIFICATION
[MS-XLS]: Excel Binary File Format (.xls) Structure
OpenOffice.org's Documentation of the Microsoft Excel File Format
FreeXL

2015-09-18

golang 的 UTF16 與 UTF8 互轉

跟〈golang 的其他語系編碼 - 以 Big5 為例 〉類似,以 golang.org/x/text 來完成這件事。
package main
 
import (
    "golang.org/x/text/encoding/unicode"
    "golang.org/x/text/transform"
    "fmt"
)

func main() {
    bs_UTF16LE, _, _ := transform.Bytes(unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder(), []byte("測試"))
    bs_UTF16BE, _, _ := transform.Bytes(unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewEncoder(), []byte("測試"))
    bs_UTF8LE, _, _ := transform.Bytes(unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder(), bs_UTF16LE)
    bs_UTF8BE, _, _ := transform.Bytes(unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewDecoder(), bs_UTF16BE)

    fmt.Printf("%v\n%v\n%v\n%v\n", bs_UTF16LE, bs_UTF16BE, bs_UTF8LE, bs_UTF8BE)
}
輸出結果。
[44 110 102 138]
[110 44 138 102]
[230 184 172 232 169 166]
[230 184 172 232 169 166]
結束。

2015-08-25

Skip a bad SSL certificate

有些政府的網站就是可以爛到讓你覺得不可思議,譬如台灣電子發票整合服務平台,在某些瀏覽器或安全機制就是不能過關,影響用程式抓資料的能力。

解法有兩種,第一種是用一般的 net/http
package main

import (
    "net/http"
    "crypto/tls"
    "fmt"
)

func main() {
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://www.einvoice.nat.gov.tw")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(resp.Status)
}
第二種是從 google app engine 抓,所以不能用 net/http,只能用 appengine/urlfetch
package gae

import (
    "net/http"
    "appengine"
    "appengine/urlfetch"
    "fmt"
)

func BadSSL(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    req, _ := http.NewRequest("GET", "https://www.einvoice.nat.gov.tw", nil)
    tr := urlfetch.Transport{Context:c, Deadline:time.Duration(30)*time.Second, AllowInvalidServerCertificate:true}
    resp, err := tr.RoundTrip(req)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
    fmt.Println(resp.Status)
}
收工。

Reference:
golang: How to do a https request with bad certificate?
GAE Golang - urlfetch timeout?

2015-06-24

golang 強大的 xml 處理能力

基本上是 reflectStruct Tags 的應用,拿來讀取 xml 更顯示出強大的威力。

如果是要該 tag 的內容,就不需要加上 attr,一切依 xml 的格式而定。
package main

import (
    "net/http"
    "fmt"
    "io/ioutil"
    "encoding/xml"
)

type FX struct {
    Currency string `xml:"currency,attr"`
    Rate string `xml:"rate,attr"`
}

type Fixing struct {
    Date string `xml:"time,attr"`
    FXs []FX `xml:"Cube"`
}

type Dates struct {
    Cube []Fixing `xml:"Cube>Cube"`
}

func main() {
    var resp *http.Response
    var data Dates

    resp, _ = http.Get("https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml")
    defer resp.Body.Close()
    XMLdata, _ := ioutil.ReadAll(resp.Body)
    xml.Unmarshal(XMLdata, &data)

    fmt.Println(data)
}
namespace 比較麻煩,目前需要加上 full path URL。
package main

import (
    "net/http"
    "fmt"
    "io/ioutil"
    "encoding/xml"
)

type DataSet struct {
    Date string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices NEW_DATE"`
    M1 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_1MONTH"`
    M3 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_3MONTH"`
    M6 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_6MONTH"`
    Y01 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_1YEAR"`
    Y02 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_2YEAR"`
    Y03 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_3YEAR"`
    Y05 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_5YEAR"`
    Y07 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_7YEAR"`
    Y10 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_10YEAR"`
    Y20 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_20YEAR"`
    Y30 string `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices BC_30YEAR"`
}

type Property struct {
    D DataSet `xml:"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata properties"`
}

type Content struct {
    P []Property `xml:"entry>content"`
}

func main() {
    var resp *http.Response
    var data Content

    resp, _ = http.Get("http://data.treasury.gov/feed.svc/DailyTreasuryYieldCurveRateData?$filter=year(NEW_DATE)%20eq%202015")
    defer resp.Body.Close()
    XMLdata, _ := ioutil.ReadAll(resp.Body)
    xml.Unmarshal(XMLdata, &data)

    fmt.Println(data)
}
收工。

2015-06-15

指令列控制 openVPN

由於 Raspbian 預設是 LXDE 界面,而目前 openVPN 只有 Gnome 的 applet,所以用指令列來控制,也比較省系統資源。

以 root 權限安裝 openvpn。
apt-get install openvpn
建一個目錄放相關文件。
mkdir /etc/openvpn
將 ca.crt 與 crl.pem 等相關驗證文件拷貝進去後,建立一個 userpass.txt 放帳號密碼。假設帳號是 username,密碼是 password。
username
password
接著修改 sample.ovpn,加入這一行來自動驗證帳號跟密碼。
auth-user-pass /etc/openvpn/userpass.txt
啟動 openVPN,以 sample.ovpn 做設定。
openvpn --config /etc/openvpn/sample.ovpn &
將 openVPN 斷線。
pkill -SIGTERM -f 'openvpn --config /etc/openvpn/sample.ovpn'
用 killall 也可以。
killall openvpn
收工。

2015-05-23

使用 ffmpeg 與 x265 轉檔 DVD

High Efficiency Video Coding

由於相較 x264 可以縮減一半以上的空間,因此頗適合拿來轉檔 DVD5 跟 DVD9,至於 720P 以上的 x264 編碼,涉及的調整比較多,不在本文範圍內。

以 root 權限安裝 ffmpeg 與 libx265。
apt-get install ffmpeg libx265-51
根據 FFmpeg and H.265 Encoding Guide 的說法,x265 的 CRF 28 等於 x264 的 CRF 23,又根據 FFmpeg and H.264 Encoding Guide 的說法,x264 的 CRF 18 相當接近於視覺上的無差別,而 DVD 的畫質約當於 480i,因此在 x265 放個 CRF 20 應該足夠,保守的話,就放個 CRF 16 或 CRF 17 吧;以下把 vob 所有的 audio 跟字幕都一併拷貝到 mkv,固定 frame rate。
ffmpeg -i input.vob -map 0 -c:a copy -c:s copy -c:v libx265 -crf 17 -vsync 1 output.mkv
轉檔時間大約是片長的一到一點五倍時間,檔案大小大約是 vob 的 20% ~ 40%,算是相當不錯。

2015-05-15

X11 Forwarding - 以 RPI2 的 Wolfram Mathematica 為例

一般來說,如果把 RPI2 當成 server 來用,開機時只有 text-based 的界面,但是我們可以遠端登入 RPI2 後,將某個 GUI 的程式執行後,把 GUI 傳到 client 端。

首先,client 端用 ssh 登入 server 端,也就是 RPI2,-X 表示啟用 X11 Forwarding。
ssh -X pi@ip.address.of.rpi2
將 /etc/ssh/sshd_config 中的加入一行;由於 RPI2 預設已經打開 X11 Forwarding,所以應該無須再做調整。
X11Forwarding yes
然後執行 Wolfram Mathematica。
mathematica
此時 client 端會出現Wolfram Mathematica Notebook,在四核心的 RPI2 下會有一點鈍鈍的;指令列的就快多了。
wolfram
收工。

2015-05-11

指令列查詢與修改某個 torrent 的內容

transmission-show 與 transmission-edit 都是 transmission-cli 的其中一個指令。

查詢 torrent 內容,包含 trackers 等資訊。
transmission-show one.torrent
查詢 torrent 的 magnet link。
transmission-show -m one.torrent
查詢目前的 peers,包括 seeders 與 leechers。
transmission-show -s one.torrent
one.torrent 加上 http://aaa.bbb.ccc:5555/announce 這個 tracker。
transmission-edit -a http://aaa.bbb.ccc:5555/announce one.torrent
one.torrent 移除 http://aaa.bbb.ccc:5555/announce 這個 tracker。
transmission-edit -d http://aaa.bbb.ccc:5555/announce one.torrent
移除所有 torrents 的 trackers。
find . -name "*.torrent" -exec sh -c 'transmission-show {} | grep "http\|udp" | sed "s/  /transmission-edit -d /" | while read line ; do $line {} ; done' \;
所有 torrent 加上 http://aaa.bbb.ccc:5555/announce 這個 tracker。
transmission-edit -a http://aaa.bbb.ccc:5555/announce *.torrent
結束。

2015-05-05

合併兩頁 A5 為 A4 - psnup

psnup 是 psutils 的一個套件,以下還需用到 ghostscript 與 pdftk;以 root 權限安裝。
apt-get install ghostscript pdftk psutils
先將 A5.pdf 轉為 A5.ps。
pdf2ps A5.pdf
將 A5.ps 兩頁 A5 印在一頁 A4 上,輸出檔名為 A4.ps。
psnup -n 2 -Pa5 -pa4 A5.ps A4.ps
將 A4.ps 轉為 A4.pdf。
ps2pdf A4.ps
收工。

Reference: [Linux] psnup:Linux下多頁列印好工具

2015-04-30

批次插入文字

假如有一大堆格式雷同的文字檔,需要在第三行加入 PlayResX: 960,並在第四行加入 PlayResY: 720。
sed -i "3i PlayResX: 960\nPlayResY: 720" *.ass
收工。

2015-03-29

密碼產生器 - pwgen

以 root 權限安裝。
apt-get install pwgen
產生五個長度十二的密碼,預設至少含一個大寫字母,-s 表示完全隨機。
pwgen -s 12 5
收工。

2015-03-17

ascii tricks: aview & FIGlet

aview

可以把圖檔轉為 ascii 格式的。

安裝。
apt-get install aview
將 test.jpg 轉為 ascii,按下 S 可以選擇如何輸出成檔案,支援 txt 或 html 等等格式,也可以設定不同的 ascii 編碼方式。
asciiview test.jpg

FIGlet

將英文轉為大型的 ascii 格式,支援多種模式。

安裝。
apt-get install figlet
將 test 轉為 ascii。
figlet test
輸出。
 _            _   
| |_ ___  ___| |_ 
| __/ _ \/ __| __|
| ||  __/\__ \ |_ 
 \__\___||___/\__|
                
顯示所有的輸出模式。
showfigfonts
收工。

2015-02-24

gedit 的替代品 - medit

medit the text editor

由於 gnome 3 日漸龐大、效能低落與不穩定,各方面也都偏向於 ubuntu 使用者,gedit 是我用的最多的 gnome 程式,所以找到替代品頗為重要。

以 root 安裝 medit。
apt-get install medit
基本上界面變化不大,各種 coding highlight 也頗完整,也有適合長時間寫程式的 Cobalt color scheme,也支援 regex(跟 gedit 的語法一樣),所以可以無痛轉換。

雖然 ubuntu 綁架了 gnome,不過替代方案多的很,gnome 就送給初學者跟 ubuntu 吧,不需要留戀。

2015-02-22

修正 ECDSA host key 問題

基本上就是兩台不同的機器使用了一樣的 IP,所以只要移除舊的 footprint 即可。
ssh-keygen -R duplicate_IP
收工。

2015-02-06

srt 轉 DVD 或 BD 字幕格式

srt2vob

bash script 格式,組合其他程式來完成,如 imagemagick、dvdauthor、mencoder、mplayer、ffmpeg。以 root 權限安裝相關套件。
apt-get install imagemagick dvdauthor mencoder mplayer ffmpeg
下載 srt2vob_0.7。
wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/srt2vob/srt2vob_0.7
變更為可執行檔。
chmod +x srt2vob_0.7
影片 clip.mkv,字幕 clip.srt,轉檔為 vobsub 格式,字型選擇 WenQuanYi Micro Hei,字體大小為 64。
font="WenQuanYi Micro Hei" fontsize=64 ./srt2vob_0.7 clip.mkv clip
然後就會生成 clip.idx 與 clip.sub 兩個 vobsub 格式的檔案,如果要改變字幕的顏色,在 palette 中有四組顏色,分別是字型的外框、字型的內部、字型的陰影、背景的底色,轉檔時預設沒有陰影,背景的底色則是沒顏色。

2015-02-01

開機自動掛載硬碟

以 root 權限查看 UUID 資訊。
blkid -s UUID
然後編輯 /etc/fstab,格式如下。
# <file system> <mount point> <type> <options> <dump> <pass>
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mount/point ext4 defaults 0 0
然後重新開機即可自動掛載硬碟。

2015-01-22

支援 Linux 的無線網卡

List of Wi-Fi Device IDs in Linux

Linux 的硬體障礙之一就是無線網卡,由於早期很多晶片不會為 Linux 提供 driver,因此要用 wifi 會很麻煩,一直都有提供 Linux driver 的是 Intel Wireless 系列,因此 laptop 要裝 wifi 反而比較容易(通常是 Mini PCI-E 界面),而 Intel Wireless 系列若要裝在 desktop 大多需要犧牲一個 PCI-E 界面,因此不是太划算。

這個清單更新算蠻快的,而且都是在 kernel 有 driver 的,所以找 wifi 設備挺方便的。

其實不要看 Linux 市占率低,很多 embedded system 都是 Linux-Like 的,未來的設備會快速增加,尤其是未來會邁向物聯網(Internet of Everything),這種低階的支援能力會決定產品滲透的速度。

2015-01-21

另一個處理 DVD 格式的軟體 - vobcopy

vobcopy

由於 lxdvdrip 很久沒更新,所以找一個備用,不過這個也有一段時間沒更新了。

以 root 權限安裝。
apt-get install vobcopy
掛載 image.iso 到 /media/cdrom。
mount -t udf -o loop,ro image.iso /media/cdrom
檢視 DVD 資訊。
vobcopy -I /media/cdrom
將最長的段落拷貝出來。
vobcopy -i /media/cdrom -l
卸載 image.iso。
umount /media/cdrom
如果還是不行的話,就手動去 /media/cdrom/VIDEO_TS 檢查,然後把要的 VOB 合併。
cat VTS_01_1.VOB VTS_01_2.VOB VTS_01_3.VOB VTS_01_4.VOB > new.vob
收工。