3
26
2014
18

百度登陆方法及网盘 API 基本操作

本文只介绍流程,因此是以最方便试错的 shell 脚本为示例的。也就是一系列简单的 HTTP 请求,用什么语言都一样。

要实践本文中的例子,首先要确保系统上已经安装了如下软件:

  • curl: 命令行 HTTP 调试首选工具
  • jshon: 命令行 JSON 解析器。使用简单的栈式语法
  • json_pp: 这个命令行工具是 perl 自带的,把 JSON 数据格式化显示用的

首先把用户信息存到变量里去:

$ BDUSER=你的百度登陆名
$  PASS=你的百度登陆密码

访问一次百度,取得一个名为BAIDUID的 cookie。我们在此,以及以下所有 curl 命令中,都会使用-b-c选项告诉 curl 从当前目录下的「cookies」文件读取 cookie 数据,把接收到的 cookie 写到同一个文件里去。

$ curl -b cookies -c cookies http://www.baidu.com/ -sS -o /dev/null

获取 token:

$ TOKEN=$(curl -b cookies -c cookies -sS "https://passport.baidu.com/v2/api/?getapi&tpl=mn&apiver=v3&class=login&tt=$(date +%s860)&logintype=dialogLogin" | tr "'" '"' | jshon -e data -e token -u)
$ curl -b cookies -c cookies "https://passport.baidu.com/v2/api/?logincheck&token=$TOKEN&tpl=mn&apiver=v3&tt=$(date +%s)&username=$BDUSER&isphone=false"
{"errInfo":{ "no": "0" }, "data": { "codeString" : "", "vcodetype" : "" }}

使用用户信息登陆:

$ curl -b cookies -c cookies --compressed -sS 'https://passport.baidu.com/v2/api/?login' -H 'Content-Type: application/x-www-form-urlencoded' --data "staticpage=http%3A%2F%2Fpan.baidu.com%2Fres%2Fstatic%2Fthirdparty%2Fpass_v3_jump.html&charset=utf-8&token=$TOKEN&tpl=mn&apiver=v3&tt=$(date +%s083)&codestring=&safeflg=0&u=http%3A%2F%2Fpan.baidu.com%2F&isPhone=false&quick_user=0&logintype=basicLogin&username=$BDUSER&password=$PASS&verifycode=&mem_pass=on&ppui_logintime=57495&callback=parent.bd__pcbs__ax1ysj" | grep -F 'err_no=400032' > /dev/null

如果这条命令返回(即$?的值)为 0 则成功,否则失败。这是因为百度的登陆之后的页面会进行跳转,如果登陆成功那么跳转 URL 里包含err_no=400032,否则err_no是别的什么值。这个判断条件可能会变化,比如去年年底是err_no=40032时表示成功。

登陆成功之后就可以调用网盘 API 了。这部分比登陆的请求要好看许多。

先来获取一下网盘容量,也好确认我们确实登陆成功了:

$ curl -b cookies -c cookies 'http://pan.baidu.com/api/quota'
{"errno":0,"total":510341939200,"used":0}

这些请求直接在网页版里开着 Firebug 看就可以了。比如:

列根目录下的文件信息:

$ curl -b cookies -c cookies 'http://pan.baidu.com/api/list' | json_pp
$ curl -b cookies -c cookies 'http://pan.baidu.com/api/list?dir=/test' | json_pp

建立目录:

$ curl -b cookies -c cookies 'http://pan.baidu.com/api/create' -F path=/测试curl -F isdir=1 -F size= -F block_list='[]' -F method=post

参考资料

Category: 网络 | Tags: 百度 curl 网页
3
25
2014
0

火狐开发者工具、Object.watch 以及地址栏图标

之前通过 userChrome.js 脚本把火狐的地址栏图标弄回来了,但是一直有这么个问题:打开一个会自动跳转的标签页,比如 t.co 或者登陆状态下的 Google 搜索结果时,首先会显示跳转前的网站的图标(这没问题),然后开始载入新页面了(这问题也不大),然后新页面是使用 HTTPS 的(比如 MDN),于是就会看到地址栏图标后边已经变成绿色、显示出了站点信息(如「Mozilla Foundation (US)」),但是图标仍旧是跳转前的图标

经过探索,我发现火狐在载入新页面时并不会及时更改网站的图标(即该 tab 对象的.image属性;大概是因为默认情况下,即标签页里那个图标,它会被「载入中」的图标取代),但是其它信息都会及时更改,这才造成了不一致。既然这段时间取不到网站的图标,那么用默认图标好了。但问题是,怎么知道地址栏的 URL 已经改变了呢?

我试过在urlbar上监听change事件。Firebug 在调试chrome://browser/content/browser.xul页面时说会有这么个事件,但是真正在 chrome 里运行时却发现没有这样的事件被触发。

还想到了通过 Object.watch 方法要监听tab.linkedBrowser.currentURI属性的更改,但是好像也没有被调用。也许那个 nsIURI 对象一直在那里,只是它的值在变而已。

期间还试过 addProgressListener。大概是用法不对,无果。

后来结合了以上两个方案,决定监听urlbar.value的更改。这下终于好用了。有点小坑的是,因为一直在尝试各种方案,没仔细看文档,结果没注意注册到.watch方法中的函数要返回实际要设置的属性值,把火狐地址栏搞坏掉了……

在调试过程中,浏览器控制台(取代了以前的「错误控制台」,快捷键还是Ctrl-Shift-J)比「代码片断速记器」好用(它实际上是火狐自带调试工具中的「Web 控制台」运行在 chrome 上的版本):

火狐的「浏览器控制台」

  • chrome 脚本中的console.log等日志会记录在浏览器控制台,并且可以按关键字过滤(比如我今天一直在用的userChrome
  • 浏览器控制台支持 Tab 补全
  • 代码片断速记器每次打开之后需要手动切换「在浏览器环境中执行」
  • 代码片断速记器中不支持选择即复制、中键点击即粘贴功能,不支持 GTK 自定义的编辑快捷键
  • 浏览器控制台中直接记录对象的话,点击之后会出现和代码片断速记器Ctrl-i快捷键一样的「对象查看器」

不过浏览器控制台不支持将日志中的对象直接在脚本中使用(Firebug 支持)。

哦还有,MozRepl 在开启 chrome 调试的时候,是连接到最后一个有焦点的火狐窗口的(而不一定是chrome://browser/content/browser.xul这个)。期间搞错了一次,发现里边竟然连gBrowser变量都没有 -_-|||

最后,辛辛苦苦接着出来的新版代码(其实没改多少):

if(location == "chrome://browser/content/browser.xul"){

  var eTLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"]
                    .getService(Components.interfaces.nsIEffectiveTLDService);

  function updateIcon(event){
    var tab = event.target;
    if(tab != gBrowser.selectedTab){
      return;
    }
    var icon = tab.image || 'chrome://mozapps/skin/places/defaultFavicon.png';
    var identity = document.getElementById('identity-box');
    if('chromeUI'.indexOf(identity.className) != -1){
      // already has a pretty icon as list-style-image
      icon = '';
    }
    console.log('updateIcon', icon, tab.linkedBrowser.currentURI.spec, event);
    document.getElementById('page-proxy-favicon').src = icon;
    if('verifiedDomain'.indexOf(identity.className) != -1){
      var identityLabel = document.getElementById('identity-icon-labels');
      identityLabel.collapsed = false;

      var domain;
      try{
        domain = eTLDService.getBaseDomain(tab.linkedBrowser.currentURI);
      }catch(ex){ // NS_ERROR_HOST_IS_IP_ADDRESS
        domain = tab.linkedBrowser.currentURI.host;
      }

      document.getElementById('identity-icon-label').value = domain;
    }
  }

  var container = gBrowser.tabContainer;
  // includes TabSelect (.selected is modified)
  container.addEventListener("TabAttrModified", updateIcon, false);
  document.getElementById('urlbar').watch('value', function(prop, oldval, newval){
    console.log('urlbar value changed to ' + newval);
    document.getElementById('page-proxy-favicon').src = 'chrome://mozapps/skin/places/defaultFavicon.png';
    return newval;
  }, false);

  })();
}
Category: 火狐 | Tags: 火狐 userChrome
3
14
2014
4

Linux 系统时间变更通知

每一次,系统从挂起状态恢复,系统日志里总会多这么几行:

systemd[1]: Time has been changed
crond[324]: time disparity of 698 minutes detected

一个来自 systemd,一个来自 dcron,都是说系统时间改变了。那么它们是怎么知道系统时间改变的呢?

dcron 的代码很少,所以很快就可以找到。因为 dcron 每一次的睡眠时长它自己知道,所以当它再次从睡眠状态醒来,发现时间变化特别大时,它就会察觉到。也就是说,小的变化它会察觉不到的。

systemd 呢?这家伙一直在使用 Linux 新加特性,比如上次发现的 prctl 的 PR_SET_CHILD_SUBREAPER 功能。这次它也没有让我失望,它使用了 timerfd 的一个鲜为人知的标志位——TFD_TIMER_CANCEL_ON_SET。timerfd 是 Linux 2.6.25 引入的特性,而TFD_TIMER_CANCEL_ON_SET这个标志位则据说 Linux 3.0 引入的,但是到目前为止(man-pages 3.61),手册里没有提到它,系统头文件里也没有它。

这个标志位是干什么的呢?其实很简单,是当系统时钟被重设时向程序发送通知,包括通过系统调用设置系统时间,以及系统从硬件时钟更新时间时。当事件发生时,在该 timerfd 上的读取操作会返回 -1 表示失败,而 errno 被设置成ECANCELED。下边是一个简单的演示程序,在系统时间变化时打印一条消息:

#include<unistd.h>
#include<sys/timerfd.h>
#include<stdbool.h>
#include<stdint.h>
#include<errno.h>
#include<stdlib.h>
#include<stdio.h>
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
#ifndef TFD_TIMER_CANCEL_ON_SET
#  define TFD_TIMER_CANCEL_ON_SET (1 << 1)
#endif

int main(int argc, char **argv){
  int fd;
  struct itimerspec its = {
    .it_value.tv_sec = TIME_T_MAX,
  };
  fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
  if(fd < 0){
    perror("timerfd_create");
    exit(1);
  }
  if(timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET,
        &its, NULL) < 0) {
    perror("timerfd_settime");
    exit(1);
  }
  uint64_t exp;
  ssize_t s;
  while(true){
    s = read(fd, &exp, sizeof(uint64_t));
    if(s == -1 && errno == ECANCELED){
      printf("time changed.\n");
    }else{
      printf("meow? s=%zd, exp=%lu\n", s, exp);
    }
  }
  return 0;
}

编译并运行该程序,然后拿 date 命令设置时间试试吧 =w= 当然记得用虚拟机哦,因为系统时间乱掉的时候会发生不好的事情喵~

date 091508002012
Category: Linux | Tags: systemd linux
3
13
2014
1

我的浏览器A到Z

Re: 的确挺好玩的~

那个 frecency 懒得去研究是干什么用的,大概是频度之类的?frecency 即是火狐地址栏著名的 frecency 算法的值。

A
标题:工作台 - Chito
URL:http://lilydjwg.is-programmer.com/admin
访问次数:1828
frecency:3564600
最后访问日期:2014-03-13 21:26:42

B
标题:搜索结果 (页 1) / Arch Linux 中文论坛
URL:https://bbs.archlinuxcn.org/search.php?action=show_new
访问次数:1550
frecency:3100000
最后访问日期:2014-03-13 21:47:33

C
标题:Twitter / Interactions
URL:https://twitter.com/i/connect
访问次数:673
frecency:1063004
最后访问日期:2014-03-13 20:30:28

D
标题:豆瓣
URL:http://www.douban.com/
访问次数:290
frecency:212507
最后访问日期:2014-02-26 21:34:39

E
标题:所有消息 - SegmentFault
URL:http://segmentfault.com/user/events
访问次数:298
frecency:539380
最后访问日期:2014-03-13 21:44:30

F
标题:Index of /ftp/python
URL:http://python.org/ftp/python/
访问次数:7
frecency:2614
最后访问日期:2014-02-19 23:45:13

G
标题:Gmail
URL:https://mail.google.com/mail/
访问次数:396
frecency:449053
最后访问日期:2014-02-28 20:26:26

H
标题:工作台 - Chito
URL:http://lilydjwg.is-programmer.com/admin
访问次数:1828
frecency:3564600
最后访问日期:2014-03-13 21:26:42

I
标题:依云's Blog
URL:http://lilydjwg.is-programmer.com/
访问次数:100
frecency:126258
最后访问日期:2014-03-09 23:28:52

J
标题:jQAPI - Alternative jQuery Documentation Browser
URL:file:///home/lilydjwg/%E6%96%87%E6%A1%A3/%E7%BD%91%E9%A1%B5/Javascript/jqapi_2013-01-21/index.html
访问次数:5
frecency:1446
最后访问日期:2013-12-18 02:25:01

K
标题:The Linux Kernel Archives
URL:http://kernel.org/
访问次数:41
frecency:33347
最后访问日期:2014-03-02 21:47:12

L
标题:Lua 5.2 Reference Manual - contents
URL:file:///usr/share/doc/lua/contents.html#index
访问次数:24
frecency:14682
最后访问日期:2014-03-05 21:47:52

M
标题:Google 地图
URL:https://maps.google.com/
访问次数:83
frecency:52035
最后访问日期:2014-03-12 19:30:50

N
标题:None
URL:http://lilydjwg.is-programmer.com/admin/posts/new
访问次数:24
frecency:33600
最后访问日期:2014-03-13 22:08:35

O
标题:查看版面 - Vim和Emacs • Ubuntu中文论坛
URL:http://forum.ubuntu.org.cn/viewforum.php?f=68
访问次数:236
frecency:166138
最后访问日期:2014-03-02 22:26:47

P
标题:Python Module Index — Python v3.3.0 documentation
URL:file:///home/lilydjwg/%E6%96%87%E6%A1%A3/%E7%BC%96%E7%A8%8B/Python/python/py-modindex.html
访问次数:124
frecency:169744
最后访问日期:2014-03-10 23:10:46

Q
标题:Qt 4.8:
URL:jar:file:///home/.ecryptfs/lilydjwg/public/%E6%96%87%E6%A1%A3/%E7%BC%96%E7%A8%8B/qt4-doc/qt4-doc.zip!/index.html
访问次数:9
frecency:5198
最后访问日期:2014-03-06 22:05:35

R
标题:InoReader • 轻便快捷的 RSS 阅读器
URL:https://www.inoreader.com/
访问次数:155
frecency:177119
最后访问日期:2014-03-12 22:41:51

S
标题:SegmentFault
URL:http://segmentfault.com/
访问次数:1486
frecency:411473
最后访问日期:2014-03-13 20:47:13

T
标题:Google 翻译
URL:http://translate.google.cn/?hl=zh-CN
访问次数:138
frecency:145314
最后访问日期:2014-03-11 23:50:21

U
标题:Pinboard: public bookmarks for vayn
URL:http://pinboard.in/u:vayn
访问次数:13
frecency:5589
最后访问日期:2014-02-16 18:42:04

V
标题:查看版面 - Vim和Emacs • Ubuntu中文论坛
URL:http://forum.ubuntu.org.cn/viewforum.php?f=68
访问次数:236
frecency:166138
最后访问日期:2014-03-02 22:26:47

W
标题:新浪微博-随时随地分享身边的新鲜事儿
URL:http://weibo.com/
访问次数:78
frecency:117933
最后访问日期:2014-02-27 22:51:10

X
标题:None
URL:http://localhost/xcache/
访问次数:6
frecency:6688
最后访问日期:2014-03-02 22:50:15

Y
标题:soimort/you-get
URL:https://github.com/soimort/you-get
访问次数:24
frecency:19374
最后访问日期:2014-03-07 21:13:24

Z
标题:消息 - 知乎
URL:http://www.zhihu.com/notifications
访问次数:528
frecency:823165
最后访问日期:2014-03-09 16:53:10


附:从 URL 生成这些数据的代码。当然,还有后期处理,是用 Vim 简单地做了几次正则替换。

#!/usr/bin/env python3

import os
import sys
import sqlite3
from time import strftime, localtime

places = os.path.expanduser('~/.mozilla/firefox/profile/places.sqlite')

def main():
  db = sqlite3.connect(places)
  sql = '''select title, visit_count, frecency, last_visit_date
           from moz_places where url = ? limit 1'''
  c = db.cursor()

  for url in sys.stdin:
    url = url.strip()
    c.execute(sql, (url,))
    title, visit_count, frecency, last_visit_date = c.fetchall()[0]
    print('''\
标题:%s
URL:%s
访问次数:%d
frecency:%d
最后访问日期:%s
''' % (title, url, visit_count, frecency,
       strftime('%Y-%m-%d %H:%M:%S', localtime(last_visit_date//1000000))))

if __name__ == '__main__':
  main()

又附:上边的代码忘记写是哪个字母了 -_-||| 已经补上,用了个 Vim 宏来完成。话说好久没用 Vim 宏了呢~

Category: 未分类 | Tags: 程序员 火狐 浏览器
3
13
2014
4

Python 3 的 super() 和 __class__

子类里访问父类的同名属性,而又不想直接引用父类的名字,因为说不定什么时候会去修改它,所以数据还是只保留一份的好。其实呢,还有更好的理由不去直接引用父类的名字,参见 Python’s super() considered super! | Deep Thoughts by Raymond Hettinger

这时候就该 super() 登场啦——

class A:
  def m(self):
    print('A')

class B(A):
  def m(self):
    print('B')
    super().m()

B().m()

当然 Python 2 里 super() 是一定要参数的,所以得这么写:

class B(A):
  def m(self):
    print('B')
    super(B, self).m()

需要提到自己的名字。这个名字也是动态查找的,在这种情况下替换第三方库中的类会出问题。

super() 很好地解决了访问父类中的方法的问题。那么,如果要访问父类的父类(准确地说,是方法解析顺序(MRO)中位于第三的类)的属性呢?

比如,B 类是继承 A 的,它重写了 A 的 m 方法。现在我们需要一个 C 类,它需要 B 类的一些方法,但是不要 B 的 m 方法,而改用 A 的。怎么间接地引用到 A 的 m 方法呢?使用self.__class__肯定是不行的,因为 C 还可能被进一步继承。

从文档中我注意到,super 的实现是通过插入一个名为 __class__ 的名字来实现的(super 会从调用栈里去查找这个 __class__ 名字)。所以,就像文档里暗示的,其实可以直接在定义方法时访问 __class__ 名字,它总是该方法被定义的类。继续我们的单字母类:

class C(B):
  def m(self):
    print('C')
    # see the difference!
    print(__class__.__mro__)
    print(self.__class__.__mro__)
    __class__.__mro__[2].m(self)

class D(C):
  def m(self):
    print('D')
    super().m()

o = D()
o.m()

会得到:

D
C
(<class 't.C'>, <class 't.B'>, <class 't.A'>, <class 'object'>)
(<class 't.D'>, <class 't.C'>, <class 't.B'>, <class 't.A'>, <class 'object'>)
A

不过,PyPy 并不支持这个 __class__ 名字。

Category: python | Tags: Python
3
9
2014
8

GM 脚本:GMail 日期正常化

我所谓的「正常化」,就是适合人而不是机器读取的格式啦。比如你们说「12/2/14」这是哪天呢……明明可以显示成「20YY年M月DD日 上午H:MM」这样子的(鼠标移过去会出现),但是为什么非要我去移鼠标呢。于是就有了这个很简单的脚本:

// ==UserScript==
// @name           GMail 日期正常化
// @namespace      http://lilydjwg.is-programmer.com/
// @description    将 GMail 中的日期使用更适合人阅读的形式显示
// @include        https://mail.google.com/*
// @grant	   none
// ==/UserScript==

var doit = function(){
  var elements = document.querySelectorAll('span[alt]');
  for(var i=0, len=elements.length; i<len; i++){
    elements[i].textContent = elements[i].getAttribute('alt');
  }
};
document.addEventListener('overflow', doit);
window.addEventListener('focus', doit);

点此安装

Category: 火狐 | Tags: gmail GreaseMonkey
3
8
2014
9

编译 Android 版 htop

编译完 lsof,却发现 htop 还是不能用……另开一个终端 strace 之,才知它竟然在调用 /bin/sh 而不是 /system/bin/sh。检查之,原来这个 htop 来自 Terminal IDE,是一个静态链接的版本,大概是使用 glibc 的吧,所以才会去标准位置寻找命令解释器。

编译过程:

path+=/opt/android-ndk/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/bin
mkdir android-build && cd android-build
CFLAGS='-I/ldata/media/temp/android/installed_binaries/include/ncurses -I/ldata/media/temp/android/installed_binaries/include' LDFLAGS=-L/ldata/media/temp/android/installed_binaries/lib CC='arm-linux-androideabi-gcc --sysroot=/opt/android-ndk/platforms/android-18/arch-arm' ../configure --host=arm-linux-gnu --prefix=/system --bindir=/system/xbin --disable-unicode
rm ../config.h

然后修改当前目录中的 config.h 文件,注释掉 HAVE_NATIVE_AFFINITY 行,添加两个 syscall 的定义,即:

// #define HAVE_NATIVE_AFFINITY 1
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set

继续 make 即可。

编译好的 htop 可由此下载

Category: Android | Tags: Android 交叉编译
3
8
2014
3

编译 Android 版 lsof

Android 自带的那个 lsof 实际上是 toolbox 里的,功能十分单一,除了显示出所有进程的所有打开的文件外就什么都不能做,连说明也没有 :-( 于是为了 htop 用着爽一点,还是自己编译一个吧。

首先弄个 GNU 工具链。Android NDK 的没有 tcp.h 头文件,会报 TCP_LISTEN 等标识符未定义。

  • 我使用 crosstool-NG 1.19.0 编译的 gcc 4.8.1。适用于 Arch Linux x86_64。第一次编译工具链,没想到在 crosstool-NG 的帮助下一次就编译好了,所以是未作静态链接的版本,其它版本的 Linux 可能无法运行。
  • zshaolin 使用的工具链 gcc 4.4.6。它是使用 crosstool-NG 1.13.2 编译的。

编译过程。使用了静态链接,最终文件大小 741K。

path+=/ldata/txtfiles/soft/arm-lilydjwg-linux-gnueabi/bin
LSOF_CC=arm-lilydjwg-linux-gnueabi-gcc LSOF_CFGF='-DHAS_STRFTIME -DHASNORPC_H -DGLIBCV' LSOF_VERS=3.0.8 ./Configure -n linux
make
arm-lilydjwg-linux-gnueabi-gcc -o lsof -static dfile.o dmnt.o dnode.o dproc.o dsock.o dstore.o arg.o main.o misc.o node.o print.o proc.o store.o usage.o util.o -L./lib -llsof
arm-lilydjwg-linux-gnueabi-strip lsof

编译好的 lsof 可由此下载

Category: Android | Tags: Android 交叉编译
3
6
2014
5

使用 PyQt 转换网页到 PDF

代码很简单,功能也很简单 =w=

#!/usr/bin/env python3

import sys

try:
  from PyQt4 import QtWebKit
  from PyQt4.QtCore import QUrl
  from PyQt4.QtGui import QApplication, QPrinter
except ImportError:
  from PySide import QtWebKit
  from PySide.QtCore import QUrl
  from PySide.QtGui import QApplication, QPrinter

app = QApplication(sys.argv)

def done(status):
  p = QPrinter()
  p.setOutputFormat(QPrinter.PdfFormat)
  p.setOutputFileName('a.pdf')
  view.print(p)
  app.exit()

view = QtWebKit.QWebView()
view.load(QUrl('http://lilydjwg.is-programmer.com/'))
view.loadFinished[bool].connect(done)
# PySide does not have QApplication.exec
app.exec_()

注意:虽然没有图形界面,但是还是需要 X 连接……

Category: python | Tags: Python PyQt Qt
3
3
2014
17

《冰雪奇缘》之缘——A站弹幕 JSON 转 crt

都是《冰雪奇缘》惹的祸,不仅画面美仑美奂,音乐激荡人心,而且还玩多语言版本的主题曲

you-get 竟助纣为虐,把歌词给下回来了!只叹 soimort 没有好人做到底,留一个语义不详的 JSON 让我情何以堪。

好在 muzuiget 才识不凡,有码略释其义,遂如我光影、声音、文字三位一体之愿!

编成代码数十行,只为女王歌一曲:

#!/usr/bin/env python3

import json
import sys

def cmtjson_reader(f):
  data = json.load(f)
  for o in data:
    c = o['c'].split(',')
    m = o['m']
    # see
    # https://github.com/muzuiget/niconvert/blob/master/niconvert/libsite/acfun.py
    start = float(c[0])
    yield start, m

def format_time(t):
  s, ms = divmod(t, 1)
  s = int(s)
  ms = int(ms * 1000)
  m, s = divmod(s, 60)
  h, m = divmod(m, 60)
  return '%d:%02d:%02d,%03d' % (h, m, s, ms)

def crt_writer(f):
  i = 0

  fmt = '%d\n%s --> %s\n%s\n\n'
  try:
    while True:
      t, m = yield
      ts = format_time(t)
      if i != 0:
        f.write(fmt % (i, format_time(old_t), ts, old_m))
      i += 1
      old_t = t
      old_m = m
  except GeneratorExit:
    f.write(fmt % (
      i+1, format_time(t), format_time(t+2), m))

def main():
  w = crt_writer(sys.stdout)
  w.send(None)
  old_d0 = 0
  for d in cmtjson_reader(sys.stdin):
    if d[0] < old_d0:
      break
    w.send(d)
    old_d0 = d[0]

if __name__ == '__main__':
  main()

mplayer 君虽识得 UTF-8 之码,却不通 fontconfig 之术,部分文字沦为了下划线。又及。

Category: python | Tags: python

部分静态文件存储由又拍云存储提供。 | Theme: Aeros 2.0 by TheBuckmaker.com