整理Bilibili安卓客户端缓存的视频

March 16, 2017
Perl

昨天晚上无意中看到了B站竟然有TOUCH。。完全没想到。所以我决定这次要把这部动画片全部下载下来,B站的视频质量看起来还不错。所以最简单的方法就是用安卓客户端缓存,然后在电脑上处理了。

其实之前我做过这件事情。当初是为了整理「暖暖日记」这部番,因为它每集仅仅有5分钟左右。每个视频只有一个文件,所以只需要找到所有视频然后读json文件找到视频名称,重命名就好了。

不过这次好像有点不同:每集一般有25分钟,缓存的是高清的。奇怪的是:即使是同一集,最开始我缓存的时候某一集是仅仅有一个mp4文件,然后我又实验了一次,发现它就变成了多个flv文件。。真是迷。。

处理也不难,如果是单个mp4文件直接重命名就好了。如果是多个flv文件,那么就需要先合并,然后再命名。

然后看了一下之前写的Perl程序,竟然,看不太懂了。。。T_T,然而还好,最后还是搞定了:

#!/usr/bin/perl

# Date  : 2017/03/16 19:14:35
# Finish: 2017/03/16 22:04:07

# NOTE: There MUST NOT be any non-ascii character in the path!!!!!!!!!!

use strict;
use warnings;
use 5.014;
use Cwd;
use JSON qw();
use open ':std', ':encoding(UTF-8)';
use File::Copy;

my $cur_dir = '/home/repl/Videos/Bilibili/Fanju/TOUCH/s_2425';
opendir(DIR, $cur_dir) or dir $!;

while (my $file = readdir(DIR)) {
    next if ($file =~ /^\./);
    
    # get index_title
    my $json_file = $cur_dir . "/$file" . '/entry.json';

    my $json_text = do {
	open(my $json_fh, "<:encoding(UTF-8)", $json_file)
	    or die("Can not open $json_file\"$!\n\"");
	local $/;
	<$json_fh>
    };

    my $json = JSON->new;
    my $data = $json->decode($json_text);
    my $index = $data->{'ep'}{'index_title'};

    # directories like 58116
    my $subdir = "$cur_dir/$file";
    opendir(SUBDIR, $subdir) or dir $!;

    while (my $subfile = readdir(SUBDIR)) {
	next if ($subfile =~ /^\./);
	if (-d "$subdir/$subfile") {
	    if ($subfile =~ /hdmp4/) {
		# only rename
		opendir(DSTDIR, "$subdir/$subfile");
		while (my $dstfile = readdir(DSTDIR)) {
		    next if ($dstfile =~ /^\./);
		    if ($dstfile =~ /mp4$/) {
			$index =~ s/ /_/g;
			rename("$subdir/$subfile/$dstfile", "$index.mp4") or die
			    "failed to copy file" . $index;
		    }
		}
		closedir(DSTDIR);
	    }
	    if ($subfile =~ /flv/) {
		# concat flv files
		opendir(DSTDIR, "$subdir/$subfile");
		my @flv = ();
		while (my $dstfile = readdir(DSTDIR)) {
		    next if ($dstfile =~ /^\./);
		    if ($dstfile =~ /flv$/) {
			push @flv, $dstfile;
		    }
		}
		@flv = sort { $a cmp $b } @flv;
		@flv = map { "file './$_'" } @flv;
		my $mylist = "$subdir/$subfile/mylist.txt";
		open (my $fh, ">", $mylist)
		    or dir $!;
		for (@flv) { print $fh "$_\n"; }
		close($fh);
		say "$subdir/$subfile";
		# output file is: output.flv. Chinese filename will crash. T_T
		system("cd $subdir/$subfile && ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.flv");
		# rename the filename of the output.flv to Chinese filename.
		$index =~ s/ /_/g;
		rename("$subdir/$subfile/output.flv", "$index.flv") or die
		    "failed to copy file" . $index;
		closedir(DSTDIR);
	    }
	}
    }
    close(SUBDIR);
}

closedir(DIR);
exit 0;

遇到几个陷阱:ffmpeg命令的输出文件不能是中文文件名,这个调试很久。然后文件路径最好不要有中文,否则出现一些奇怪的问题,不知道怎么解决,都改成英文名就好了。

哦,还有一件事情:以前试过很多种从Android传数据到Linux的方法,最后还是发现,adb pull最好用,速度最快。比如:

adb pull /storage/emulated/0/Android/data/tv.danmaku.bili/download/s_2425
comments powered by Disqus