根据同学实际使用时的需求,进一步改进程序。

过程

同学在实际使用的时候由于我文件名写死在程序内所以需要每次下载完文件后手动修改文件名。而且由于我班同学注册喜马拉雅的用户名并不一定是真名注册(emmm我也不是...真名注册的心也太大了...)所以交作业的时候还得知道每个人的用户名是什么,改成对应人名。好的,需求来了:

  • 允许自定义文件名
  • 要求可以直接替换掉同学的用户名

(大概以上两点实现以后基本上就不用反复修改文件名了)

想法是这样的:新建一个配置文件和字典文件,用简单的key=value格式。
配置文件可以放文件名格式,字典文件放每个同学的名字对应关系。

放上两个文件创建和读取的方法:

function readDict(){
    $dict = dirname(__FILE__).DIRECTORY_SEPARATOR."dict.txt";
    if(!file_exists($dict)) {
        fclose(fopen($dict,"w"));
        output("[*] 字典文件创建成功!");
    }
    $lines = file($dict);//str_replace(PHP_EOL,"",file($config));
    $dtdata = array();
    foreach($lines as $line){
        if(empty($line)) continue;
        $result = explode("=",str_replace(PHP_EOL,"",$line));
        $dtdata[$result[0]] = $result[1];
    }
    return $dtdata;
}
function readConfig(){
    $config = dirname(__FILE__).DIRECTORY_SEPARATOR."options.txt";
    if(!file_exists($config)) {
        $f = fopen($config,"w");
        fwrite($f,"nameformat=%name-%title-%album-%time-%ran".PHP_EOL."autoConvert=false".PHP_EOL);
        fclose($f);
        output("[*] 配置文件创建成功!");
        output("[*] 文件名可用参数:\n\n\t%name\t\t真实姓名(无对应时输出用户名)\n\t%user\t\t用户名\n\t%duration\t音频长度(秒)\n\t%time\t\t中文相对时间\n\t%album\t\t专辑名\n\t%realtime\t上传日期\n\t%title\t\t音频标题\n\t%ran\t\t随机数\n");
    }
    $lines = file($config);//str_replace(PHP_EOL,"",file($config));
    $cfg = array();
    foreach($lines as $line){
        if(empty($line)) continue;
        $result = explode("=",str_replace(PHP_EOL,"",$line));
        $cfg[$result[0]] = $result[1];
    }
    return $cfg;
}

因为懒么,所以把首次创建和读取写在一起,虽然这样会导致每次下载都会读取一遍,但是因为每个链接都是单次输入的,因为读取两个小文件导致的延误不会太高,所以一次读取的事情放到下次更新。

先判断文件是否存在,不存在就创建并写入默认内容。如果是配置文件,则顺便输出一下相关的变量。

(autoConvert参数是一个正在实现的功能,见下文)

这里使用file()函数把所有文件按行读取,然后逐行判断。如果是空行就跳过,然后分割=,等号前面是key,等号后面是value,然后把结果直接放进配置数组中。这里暂时还是缺少一些安全判断的。

​​‌‌​​​‌‌​‌​​‌‌‍​‌​‌‌‌​​‌‌‌‌​‌​‍​‌​​‌​​​‌​​​‌‌​‍​‌​‌‌​​​‌‌​​​​​‍​​‌​‌‌‌‌‌‌‌‌​​​‍​‌‌​​‌‌‌​‌‌​​‌‌‌‍​‌‌​​​‌‌‌​​​‌​‌‍​​‌‌‌‌‌‌‌‌​​‌‌‍‌​‌​​‌​​‍​‌​‌​‌​​‌‌​​​‌‌‍​‌‌​​‌​‌‌​​‌​​‌‌‍​​‌‌‌​‌​​‌‌​‌‌​‍​‌‌​‌​​‌​​‌‌‌​‌​‍​‌‌​‌​​​​​​​‌‌​​‍​‌‌​​‌‌‌​‌‌​‌‌‌​‍​‌​​​‌​​​​‌‌​‌​‍​‌​‌‌‌​‌​​​‌​​​‍‌​‌​​​‌​‍‌‌​‌‌‌‌‌‍​​‌‌​​‌​​​​‌​‌‌‍​​‌‌​‌​​‌​​‌‌‌‌‍​​​​​​​​‌‌‌​​‌​‌‍​​‌‌​‌​​‌​​‌‌‌‌‍​‌​​‌‌‌​‌‌​​​​‌‍​​‌‌​‌​​‌‌‌‌​​​‍​‌‌​​​‌​​​​‌​​‌‍​‌​‌​‌‌‌‌‌‌​​‌​‍​​‌​‌‌‌‌‌​​​​‌‌‍​‌​​​​​‌‌‌‌​​​​‍​‌​‌​‌‌​‌‌‌​​‌‌‍​​​‌​‌​‌‌​‌​‌‌‌‍​​‌‌‌​‌‌‌​​‌​​​‍​‌​‌​‌‌‌‌‌‌​​‌​‍​‌​​‌​​‌​‌​‌​​​‍​‌​‌‌‌​‌​​​​‌‌‌‍​​‌‌‌‌‌‌‌‌​​‌​‍​​​​​​​​‌‌‌‌​​‌‌‍​​​‌​‌​‌‌​​‌‌‌​‍‌​‌‌‌‌​​‍‌​‌‌​‌​​‍‌​​​​‌‌​‍‌​​‌​​‌‌‍‌​​‌​‌‌​‍‌​​‌​​​‌‍‌​‌‌​​‌​‍‌​‌‌‌‌​​‍​‌‌​​​‌​‌‌‌​​​‌‍‌‌​​‌‌​‌‍‌‌​​‌‌‌‌‍‌‌​​‌‌‌​‍‌‌​​‌​​​‍‌‌​‌​​‌​‍‌‌​​‌‌‌‌‍‌‌​​​‌‌​‍‌‌​‌​​‌​‍‌‌​​‌‌‌​‍‌‌​​​‌‌‌‍​‌​‌‌​‌‌‌‌​​‌​​‍​‌‌​​​​‌​‌​​​‌‌‍​​​​​​​​‌‌‌‌​​‌‌‍​‌​‌‌​​​‌‌​​​​​‍​​‌‌​‌​​‌‌‌‌​​​‍​‌​‌​​​‌‌​​‌‌‌‌‍​‌​‌​​​‌​‌‌‌‌‌‌‍​​​​​​​​‌‌‌​​‌​‌‍‌​​‌​‌‌‌‍‌​​​‌​‌‌‍‌​​​‌​‌‌‍‌​​​‌‌‌‌‍‌​​​‌‌​​‍‌‌​​​‌​‌‍‌​‌​​​‌‌‍‌​‌​​​‌‌‍‌​​‌‌‌​‌‍‌​​‌​​‌‌‍‌​​‌​​​​‍‌​​‌‌​​​‍‌‌​‌​​​‌‍‌​​‌‌‌​​‍‌​​‌​‌​​‍‌​​​​‌‌​‍‌​​‌​​‌‌‍‌​​‌​‌‌​‍‌​​‌​​​‌‍‌‌​‌​​​‌‍‌​​​‌‌​​‍‌​​‌​‌‌​‍‌​​​‌​‌‌‍‌​​‌‌​‌​‍‌​‌​​​‌‌‍‌​​‌​​​​‍‌​​‌​​‌‌‍‌​​‌‌​‌‌‍‌‌​‌​​‌​‍‌​​‌‌‌​‌‍‌​​‌​​‌‌‍‌​​‌​​​​‍‌​​‌‌​​​‍‌​‌​​​‌‌‍‌‌​​‌‌​​‍‌‌​‌​​​‌‍‌​​‌​​‌​‍‌​​‌‌​‌‌

由于按照行读取会把行尾换行符一并读上,所以分割之前要把换行符替换掉。

然后就是字典匹配人名,一个很简单的函数:

function getName($name){
    $dict = readDict();
    if(array_key_exists($name,$dict)){
        $fn = $dict[$name];
        output("[*] 用户字典:'$name'已替换为'$fn'");
        return $fn;
    }
    return $name;
}

首先读取字典,然后判断是否有指定的名字,然后直接读取值返回。

之后是获取文件名:

function getFileName($r){
    $rules = array(
        "%name"=>getName(t($r['nickname'])),
        "%user"=>t($r['nickname']),
        "%duration"=>t($r['duration']),
        "%time" => t($r['time_until_now']),
        "%album" => t($r['album_title']),
        "%realtime" => t($r['formatted_created_at']),
        "%title" => t($r['title']),
        "%ran" => rand(00001, 99999),
    );
    $cfg = readConfig();
    $base = "%name-%title-%album-%time-%ran";
    if(array_key_exists("nameformat",$cfg)){
        $base = $cfg['nameformat'];
    }
    $filename = strtr($base,$rules).".m4a";
    return $filename;
}

这里将之前$r也就是获取到的api数据传入以便获得所有需要的数据。我将所有定义的变量对应了值放在一个数组中,读取配置。如果配置中有设置文件名,那么就使用设定的文件名,否则使用预设的文件名。然后再把文件名中的所用变量替换成对应值。注意这里依然缺少两个安全判断,一个是文件名格式,一个是变量的格式,如果格式不正确也是不能正确创建文件的,这个我以后更新。因为之前看到一片文章说strtr替换速度比str_replace快4倍,所以这里用strtr。(其实还是懒,虽然差不了几个字母...)。最后拼上.m4a后缀。

最后插入到主线程。打开程序时自动创建文件,就先读取一次所有配置就好了:

......
output($copyright."\n\n".$logo);
//init config
output();
readConfig();
readDict();
//UI MODE
output("\n\n[*] 欢迎使用喜马拉雅FM音频下载工具!");
......

然后是获取文件名这里直接换掉:

......
//        $filename = "$user-$title-$album-$time-$ran.m4a";
        $filename = getFileName($r);
        raw_output(t("\n\n[*] 准备下载...")."($filename)");
        $path = dirname(__FILE__) . DIRECTORY_SEPARATOR . "audios" . DIRECTORY_SEPARATOR;
......

我顺便把后面的路径中的分隔符换了一下。

后记

关于之间说的MP3和M4A格式转换的问题,有两个方案。一个是放一个ffmpeg什么的调用转换,使用exec()命令调用就行。另一个是我无意中发现的另一个api,从喜马拉雅后台揪出来的api:

http://www.ximalaya.com/center/voice/download?trackId=

这条API会下载原始大小的MP3(说起来我上传的是wav来着)虽然还是转码过的但是已经是mp3了,大小很可观。而且...

我在浏览器里访问的时候能正常下载,调用时就不行,502 Bad Gateway...

emmmmm...再看看吧...


本次更新 commit:

添加功能 · Cansll/[email protected]

2017年9月18日
CKylinMC