2012年12月19日 星期三

[Chrome] Nico Download Manager (nicoDLM)

上午8:06
Chrome Extension - nico Download Manager
前言:
嗯..因為作者本身常常逛nico,

chrome好像沒有類似nicofox這套件能下載動畫 & comment

所以就....(ry


套件名稱:
nico Download Manager ( ニコ動 ダウンロード管理員 )

Version: 0.1.7 (2013/11/20)

Version: 0.1.6 (2013/2/11)
Version: 0.1.5 (2013/1/8)
Version: 0.1.4 (2012/12/26)
Version: 0.1.3 (2012/12/21)
Version: 0.1.2 (2012/12/20)
Version: 0.1.1 (2012/12/19)


下載點(download):
http://fs.tkafu.cc/nico_DLManager-0.1.7.crx (latest)

目前因為chrome把非app store的套件都會停用,目前還在解決中
暫時性的解決方式:
  • http://fs.tkafu.cc/nicoDLM_src.zip下載並解壓縮
  • 從chrome的工具 > 擴充功能 (或是在網址輸入 chrome://extensions ),打開"開發人員模式" > "載入未封裝擴充功能",選擇剛剛解壓縮的資料夾



0.1.7修正:
  • 修復 右鍵選單有時候會消失的BUG。
  • 修復 社群的影片會無法下載。(影片ID全為數字的情況。)

安裝方式:
1. 右鍵另存.crx檔案。
2. 開啟"工具" -> "擴充功能" 的頁面,再把.crx拖曳到chrome安裝即可。


source code at Github
https://github.com/kilfu0701/nicoDLM


使用介紹:
1. 下載.crx之後,安裝到chrome 會多出一個extension icon在右上角


2. 點選icon 會出現一個頁面,裡頭有下載清單、一般設定、語言環境、關於本套件的表格。


3. 設定調整完畢儲存後,開啟nico的影片頁面,下方會出現一個綠色的框。點選此連結,即可下載動畫以及評論。


3. 因為原宿版本要結束了,之後就要改成GINZA版本了!
不過,還是可以用0.1.6的右鍵選單下載動畫


4. 回到套件的下載清單裡,會看到剛剛新增進去的影片,
(影片名稱、敘述、下載進度、屬性等等)


5. 影片品質,可以從進度下方看到一個"HI"或是"LOW"的小圖示,
HI表示高品質、LOW則是低品質(也就是所謂的經濟模式..)

旁邊的"資料夾"小圖示,則是可以開啟檔案所在的目錄
(若檔案不存在時,不會開啟資料夾)


誰適合使用此套件:
經常逛nico、看nico影片的人


誰不適合使用此套件:
嗯?? Nico是甚麼?好吃嗎?(誤很大)


備註:
1. 請正確設定 下載資料夾 的位置,若有啟用UAC的功能,可能會導致檔案無法寫入!

2. 目前只設定最多兩個影片同時下載。

3. 目前尚未加入下載暫停/取消的功能,若想取消正在下載的檔案,可以將套件重啟。

4. 如果下載影片,發生403 forbidden時,建議把該影片的網址 重新再新分頁開啟後,在重新下載。

5. 檔案格式命名,目前沒有多餘的設定可改,暫時只提供%ID%和%TITLE%。

6. 目前只提供Windows的平台,但是沒有各種環境測試過,不確定有沒有相容問題。

7. 原宿版要結束了,GINZA版本 依舊可以用右鍵選單進行下載。



嗯...有發現BUG或是其他功能,可以回報給我。

2012年11月8日 星期四

[Python] [ニコニコ] Nico影片的下載 & 影片網址解析

凌晨3:08
無聊用python寫一個小東西,主要是解析nico的url,找出影片的實際位置,
加入下載影片的功能 @ 2013/1/7

使用方法
目前可以用兩種方法取得URL,

第一種是給整個url的..用 Nico.req('http://www.nicovideo.jp/watch/sm19186604')
第二種是只給影片的ID Nico.reqSM('sm19154040')

新增Nico.DLVideo( 'http://xxx' ),主要是下載影片 XD


程式碼
# -*- coding: utf-8 -*-
import os, sys
import urllib, urllib2, cookielib
from datetime import *
import time

"""
    Get nico video's link.
    Save cookie in a file, for repeating request API server.
"""
class Nico:
    def __init__(self, settings={}, cookieFile='nico_cookies.lwp'):
        self.cj = cookielib.LWPCookieJar()   # cookie
        self.sm = 0                          # video ID
        self.ck_file = cookieFile            # cookie file
        self.header =  {'User-agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'} # header
        if settings:
            self.login_data = settings       # account / pw
        else:
            self.login_data = {
                'mail' : 'your_account',
                'password' : 'your_password',
            }

        self.flapi_info = {}
        self.post_url = 'https://secure.nicovideo.jp/secure/login?site=niconico'  # login page url
        self.api_url = 'http://flapi.nicovideo.jp/api/getflv?v={0}'               # api url
        self.thumb_url = 'http://ext.nicovideo.jp/api/getthumbinfo/{0}'           # thumb url
        self.page_url = ''                                                        # video page url
        self.video_url = ''                                                       # video real link
        self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cj))
        self.domain = '.autocookie.tt.vv'                                         # define a doamin
        self.cookie_expires = 60*60*24*30  # save cookie in a month

        urllib2.install_opener(self.opener)

        ## if cookie file exist, load it
        if os.path.isfile(self.ck_file):
            self.cj.load(self.ck_file)
            if self.validate()==False:  ## after load cookie file, validate it.
                self.get_cookie()       ## invalid, get a new cookie.
        else:
            ## try to get a new cookie
            self.get_cookie()

    """
        if cookie files exists, validate cookies first!

        @return  : bool
    """
    def validate(self):
        try:
            _user = self.get_value('user', self.domain)
            if len(_user)==1 and _user[0]==self.login_data['mail']:
                return True
            else:
                ## custom cookie value not found / account not match, maybe changed.
                return False

        except Exception, e:
            print e
            return False          

    """
        Dump cookie's data.
    """
    def pr_cookie(self):
        for index, cookie in enumerate(self.cj):
            print index, '  :  ', cookie
            print '%s --> %s' % (cookie.name, cookie.value)

        for d in self.cj._cookies:
            print d

    """
        Get a cookie from nico.
    """
    def get_cookie(self):
        self.cj.clear()
        post = urllib.urlencode(self.login_data)
        req = urllib2.Request(self.post_url, post, self.header)
        resp = urllib2.urlopen(req)
        if resp.headers['x-niconico-authflag']=='1':
            print '== get cookie from NICO success =='
            self.add_custom_cookie("user", self.login_data['mail'])   # add a custom cookie
            self.cj.save(self.ck_file)

        elif resp.headers['x-niconico-authflag']=='0':
            print '== Error: Failed getting cookie. Check ID/PW is correct =='
            self.cj.save(self.ck_file)
            sys.exit()
        else:
            print '== Error: cookie format error. =='
            sys.exit()

    """
        get value from cookies.

        @name   : index
        @domain : if domain not set, it will find all cookies without domain

        @return : a list
    """
    def get_value(self, name, domain=''):
        ret = []
        a = 0
        if domain=='':
            for c in self.cj:
                if c.name == name:
                    ret.append(c.value)
        else:
            if len(self.cj._cookies[domain]).__class__ == a.__class__:
                if self.cj._cookies[domain]["/"][name].name == name:
                    ret.append(self.cj._cookies[domain]["/"][name].value)
            else:
                pass

        return ret

    """
        add a custom datas into cookie, for validate user account

        @name  : 
        @value : 
    """
    def add_custom_cookie(self, name, value):
        _expires = int(time.time()) + self.cookie_expires

        self.cj.set_cookie(
            cookielib.Cookie(
                version=0,
                name=name,
                value=value,
                port=None,
                port_specified=False,
                domain=self.domain,
                domain_specified=True,
                domain_initial_dot=False,
                path="/",
                path_specified=True,
                secure=False,
                expires=_expires,
                discard=False,
                comment=None,
                comment_url=None,
                rest={}
            )
        )

    """
        Start request / find the nico video original link.

        @param : post data
        @url   : the nico video's URL
    """
    def req(self, url='', param={}):
        try:
            self.sm = url.rsplit('/', 1)[1]   # get ID
            return self.reqSM(self.sm, param)

        except Exception, e:
            print '== Error: cannot parse video ID in link =='
            sys.exit()

    """
        If already have video ID, than get video link by ID.

        @param : post data
        @sm    : video ID
        @retry : if failed, retry again.
    """
    def reqSM(self, sm='0', param={}, retry=True):
        try:
            self.sm = sm
            self.page_url = "http://www.nicovideo.jp/watch/{0}".format(self.sm)
            
            url = self.api_url.format(self.sm)
            post = urllib.urlencode(param)
            resp = self.opener.open(url, post)
            data = resp.read()
            for i in data.split('&'):
                x = i.split('=')
                self.flapi_info[x[0]] = x[1]

            if 'url' in self.flapi_info:
                self.video_url = self.flapi_info['url']
            else:
                if retry:
                    self.get_cookie()                                   # get a newer cookie
                    self.video_url = self.reqSM(self.sm, param, False)  # req once again
                else:
                    self.video_url = ''

            self.video_url = urllib2.unquote(self.video_url.encode("utf8"))
            return self.video_url

        except Exception, e:
            print '== Error: cannot reverse the video url =='

    """
        Download video. 
            Before doing download, make sure "req()" or "reqSM()" first! 
    """
    def DLVideo(self, url=''):
        if url=='':
            print "Video URL is empty!"
            return False
        
        if self.page_url=='':
            print "Video page link not set. Please run req()/reqSM() first."
            return False
        
        try:
            from BeautifulSoup import BeautifulSoup as Soup
        except:
            print "'BeautifulSoup' libary not found."
            return False
        
        # get thumb info
        resp = self.opener.open(self.thumb_url.format(self.sm))
        data = resp.read()
        soup = Soup(data)
        file_ext = soup.find('movie_type').text
        
        
        # open original page first
        self.opener.open(self.page_url)

        file_name = "{0}.{1}".format(self.sm, file_ext)
        u = urllib2.urlopen(url)
        f = open(file_name, 'wb')
        meta = u.info()
        file_size = int(meta.getheaders("Content-Length")[0])
        print("Downloading: {0} Bytes: {1}".format(url, file_size))

        file_size_dl = 0
        block_sz = 1024
        while True:
            buffer = u.read(block_sz)
            if not buffer:
                break

            file_size_dl += len(buffer)
            f.write(buffer)
            p = float(file_size_dl) / file_size
            status = r"{0}  [{1:.2%}]".format(file_size_dl, p)
            status = status + chr(8)*(len(status))
            sys.stdout.write(status)

        f.close()
        print "Complete!  -->  {0}".format(file_name)
        return True
        
"""
    usage:  
        python nicoDLM.py sm123456 sm345678    .....
"""
if __name__ == "__main__":
    cookie_path = 'nico_cookies.lwp'  # cookie's location
    myset = {
        'mail' : '',
        'password': ''
    }
    
    #nico = Nico(cookieFile=cookie_path, settings=myset)
    #print nico.req('http://www.nicovideo.jp/watch/sm19186604')
    #print nico.reqSM('sm19154040')
    #nico.DLVideo( nico.reqSM('sm18684948') )
    
    num = len(sys.argv)
    if num==1:
        print "no input for download"
    else:
        nico = Nico(cookieFile=cookie_path, settings=myset)
        for i in xrange(1, num):
            nico.DLVideo( nico.reqSM(sys.argv[i]) )
    
    


結論
恩,應該先寫到這邊,

有新的功能在慢慢加近去。XD

2012年8月29日 星期三

[Shell Script] 備份檔案

凌晨12:40
備份用

耶,就隨意寫一個shell script搭配crontab

備份自己server上的一些東西...(茶)


程式碼部分
#/bin/bash
# ./backup.sh -p /var/www/sc/ -t /tmp

function usage ()
{
    echo -e "** param error. **"
    echo -e "usage:"
    echo -e "\tbackup --path /var/www/kilfu --to /var/www/"
    echo -e "\tbackup -p /var/www/kilfu -t /var/www/\n"
    echo -e "param:"
    echo -e "\t-p | --path\t\tfolder's path."
    echo -e "\t-t | --to\t\tthe path where compress into."
    exit
}

function check_folder_exist ()
{
    if [ -d "$1" ]; then
        return 1
    else
        echo -e "[$2] folder not found!"
        exit
    fi
}

if [ $# -le 0 ]; then
    usage
fi

while [ "$1" != "" ]; do
    case $1 in
        -p | --path )           shift
                                _path=$1
                                ;;
        -t | --to )             shift
                                _to=$1
                                ;;
        -h | --help )           usage
                                exit
                                ;;
        * )                     usage
                                exit 1
    esac
    shift
done

if [ $_path == "" ]; then
    $_path
fi


check_folder_exist $_path "path"
check_folder_exist $_to "to"

TODAY=`date +%Y%m%d`
IFS="/"
export IFS;
for word in $_path; do
    if [ "$word" != "" ]; then
        fname="$word"
    fi
done

cd "$_to"
echo -e "Start compressing..."
echo -e "  compress folder:\t$_path"
echo -e "  destination:\t\t$_to"

tar zcvfP "${fname}_${TODAY}.tar" "$_path"


使用方法
1:
/bin/bash backup.sh -p /var/www/sc/ -t /tmp
2: crontab
#分 時 日 月 週
# backup sc
30 3 * * * /bin/bash /var/www/sc/backup.sh --path /var/www/sc --to /home/backup/


恩,就這樣。

2012年6月21日 星期四

[Django] 建立一個google map

凌晨3:02
參考資料(references):
https://docs.djangoproject.com/en/dev/ref/contrib/gis/install/
http://jefurii.cafejosti.net/blog/2011/05/05/basic-google-maps-django/

安裝設定

先安裝geos吧,這邊是用source code自行編譯:

下載/解壓縮: (解壓縮有錯誤訊息,可能沒裝bzip2.. apt-get install bzip2)
wget http://download.osgeo.org/geos/geos-3.3.0.tar.bz2
tar xjf geos-3.3.0.tar.bz2

編譯+安裝: (編譯需要一段時間,大概5~10分鐘吧)
cd geos-3.3.0
./configure
make
sudo make install

安裝完成後,請打ldconfig,把剛剛安裝好的lib讀取進來
sudo ldconfig

django部分

先在你的app/views.py加入
from django.conf import settings
from django.shortcuts import render_to_response
from django.contrib.gis.maps.google.gmap import GoogleMap
from django.contrib.gis.maps.google.overlays import GMarker, GEvent

def google_map(request):
    points = [
        {'lat':'35.42',     'lng': '139.42', 'href':'http://en.wikipedia.org/wiki/Tokyo'},
        {'lat':'51.30',     'lng':   '0.73', 'href':'http://en.wikipedia.org/wiki/London'},
        {'lat':'40.43',     'lng': '-74.0',  'href':'http://en.wikipedia.org/wiki/New_York_City'},
        {'lat':'34.03',     'lng':'-118.15', 'href':'http://en.wikipedia.org/wiki/Los_Angeles'},
        {'lat':'36.774402', 'lng':'-119.755405', 'href':'http://en.wikipedia.org/wiki/Fresno'},]
    markers = []
    for point in points:
        marker = GMarker('POINT(%s %s)' % (point['lng'], point['lat']))
        event = GEvent('click', 'function() { location.href = "%s"}' % point['href'])
        marker.add_event(event)
        markers.append(marker)
    google = GoogleMap(center=(0,0), zoom=1, markers=markers,
                       key=settings.GOOGLE_MAPS_API_PASSWORD)
    return render_to_response('google_map.html',
                              {'google': google,})

然後建立一個template檔案為 google.map.html
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
{{ google.xhtml }}
<head>
  {{ google.style }}
  {{ google.scripts }}
</head>
<body onload="{{ google.js_module }}.map_load()" onunload="GUnload()">
  <div id="{{ google.dom_id }}" style="width:600px; height:400px;"></div>
</body>
</html>

然後到settings.py加入:
INSTALLED_APPS = (
    ...
    'django.contrib.gis',
    ...
)

GEOS_LIBRARY_PATH = '/usr/local/lib/libgeos_c.so'
GOOGLE_MAPS_API_PASSWORD = 'asdasdasdasdasd'

這邊需要google map api v2的key,由於申請方式和以前不同,

需要用到信用卡刷帳單....(雖然都是0元...需求過大的話,才須額外付費)
https://code.google.com/apis/console/

以上寫完,別忘了去設定urls.py

再去看看網址有沒有地圖出來囉。

2012年4月4日 星期三

[Debian] PPTP VPN 防火牆設定 for DNF (日版Arad)

晚上10:14


關於VPN的初始設定和架設,可以看我之前的[Debian] VPN架設 這篇文章。


恩,原本的iptables設定 基本上都可以達到VPN的功用...

但是,我在測試日版ARAD的組隊的時候,總是不夠順暢,

我也知道這遊戲組隊用到的port是UDP 5063,

中間一直用codomo的防火牆來觀察連線的狀態,

經過兩天的測試,才發現需要在iptables多一些prerouting的規則,

不然,A電腦和B電腦都用VPN組隊時,UDP的封包沒辦法轉送給對方,

導致可以組隊,但是會不夠順暢...(偶而會出現電腦的小圖示)

像是下面的圖:

┌----> VPN Server <-----┐
| |
| port:5063 | port:5063
| udp(out) | udp(out)
A B


會變成兩邊的遊戲端,都會向對方傳送組隊的封包,

VPN Server卻沒有轉送給對方,造成UDP封包送不到對方的遊戲端...(昏)
(PS: UDP的運作原理就不用多說了,請參考Google囉)



後來,我把iptables的設定改成以下:
# clean all setting
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

# DNF port pre-routing
iptables -t nat -A PREROUTING -p udp --dport 5063 -i eth0 -j DNAT -s 0/0 --to 192.168.10.234
iptables -t nat -A PREROUTING -p udp --dport 5063 -i eth0 -j DNAT -s 0/0 --to 192.168.10.235
iptables -t nat -A PREROUTING -p udp --dport 5063 -i eth0 -j DNAT -s 192.168.10.234 --to 0.0.0.0-255.255.255.255
iptables -t nat -A PREROUTING -p udp --dport 5063 -i eth0 -j DNAT -s 192.168.10.235 --to 0.0.0.0-255.255.255.255

iptables -I FORWARD -p tcp --syn -i ppp+ -j TCPMSS --set-mss 1356
iptables -o eth0 -A FORWARD -p tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 800:1536 -j TCPMSS --clamp-mss-to-pmtu


主要是DNF port pre-routing那四行,VPN Server把接收到port為5063的UDP封包,

做轉送的動作,像是下面的圖:


A傳給B:
┌----> VPN Server ------┐
| |
| port:5063 | port:5063
| udp(out) ↓ udp(in)
A B

B傳給A:
┌----- VPN Server <-----┐
| |
| port:5063 | port:5063
↓ udp(in) | udp(out)
A B


這樣就解決了ARAD組隊不順暢的問題了,

本來還在想說,遊戲連線會很聰明的直接在A和B建立一個UDP,

就不需要在經由VPN Server說...(汗)


PS: 至於可不可以和外面的人組隊的話,我還沒測試過,基本上應該沒問題才對。
但是,如果組隊雙方都是用VPN連線的話,
就需要確定提供VPN主機的人,防火牆設定有沒有調整好,

不然,只有一方的VPN有設定,另一方沒設定,
也是會有不順暢的問題!
(這我測過了,A電腦用別人家的付費VPN,B電腦是連我自己架設的VPN)

至於別人家的VPN幫不幫你設定,只好看對方的誠意了。



2012年4月3日 星期二

[Debian] VPN架設

凌晨3:50


恩...最近買了一個VPS來用拉!!!

第一件事就是先架VPN來用囉....

基本上,我是參考 這個網站 的教學

apt-get update
apt-get upgrade
apt-get install pptpd


移除這兩個#的註解
vim /etc/pptpd.conf

localip 192.168.0.234-238,192.168.0.245
remoteip 192.168.1.234-238,192.168.1.245


VPN連線時的帳號密碼,請自己設定囉
vim /etc/ppp/chap-secrets
# Secrets for authentication using CHAP
# client        server  secret                  IP addresses
fu              pptpd   passwordhere            *


把#註解拿掉
vim /etc/sysctl.conf
net.ipv4.ip_forward=1


會出現剛剛你拿掉註解的那行,沒出現的話就是有錯誤,回去看看有沒有修改到
sysctl -p


iptables的設定,他的網站"減號"有大小寫的問題,當初改了一下才成功,沒出現錯誤。
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -o eth0 -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
-m tcpmss --mss 800:1536 -j TCPMSS --clamp-mss-to-pmtu


chmod +x /etc/init.d/pptpd
/usr/sbin/update-rc.d -f pptpd defaults


設定防火牆 iptables的設定
vim /etc/iptables.sh
#!/bin/sh
IPT="/sbin/iptables"
$IPT -t nat -A POSTROUTING -o eth0 -j MASQUERADE
$IPT -o eth0 -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
-m tcpmss --mss 800:1536 -j TCPMSS --clamp-mss-to-pmtu


chown root /etc/iptables.sh
chmod 700 /etc/iptables.sh


加入 pre-up /etc/iptables.sh
vim /etc/network/interfaces
pre-up /etc/iptables.sh


重新開機,測試看看能不能連囉。