Mac字幕自动下载器

射手影音最近做了一件很有争议的事情:将SPlayerX摆上了Mac AppStore。SPlayerX是一个Fork自MPleryX的影音播放器。这次射手为了完全规避GPL协议的限制,将字幕下载部分功能用二进制可执行程序方式释出,以fork运行的方式调用这个名为sscl的二进制文件下载字幕。

姑且不论其将二进制文件与GPL协议软件捆绑发行是否违反GPL协议以及GPL协议软件与Mac AppStore的协议是否有冲突via。射手这次的做法恰好为所有Mac用户送上了sscl这个大礼,可以使用这个工具自动匹配本地硬盘的视频文件。

原来的设想是用AppleScript写一个脚本,并在Finder里自动监控特定目录,这样每当某目录下有新视频文件添加时就会自动下载字幕。但是我对AppleScript实在不熟悉,加上相关参考资料太少,遂作罢用bash写了一个脚本。如果有AppleScript高手,请改写之:-)

果然抛砖引玉,请参考利用Hazel让Mac自动下载电影字幕更加自动化进行字幕下载操作:-)

sscl的字幕下载目的目录是固定的,所以脚本里就直接Hardcode进去了。

注意: 射手影音并没有为sscl单独设置一个license,我只能根据SPlayerX本身免费下载认为它是freeware。提取该文件单独运行引起的版权上的争议不在本文讨论范围内。 本文中提到的find、mktemp为GNU findutils和GNU coreutils里的版本,请通过MacPort或其他Port系统安装之后再执行

功能描述 执行脚本时,在预设的目录内查找mkv和avi文件,过滤掉某些0day release里的sample文件。 对于每个文件,如果不存在对应的.chn.srt字幕文件,就调用sscl进行下载并复制到电影文件所在文件夹。

使用方法

  1. 下载sscl文件。该文件可以从SPlayer Mercurial源里获取,地址是:http://hg.splayer.org/splayerx/raw/0b9e84441210/binaries/x86_64/sscl
  2. 修改该文件权限,终端下chmod +x sscl
  3. 将以下代码保存为sub.sh。请根据你系统内的bash路径修改shabang,我的系统bash是在/Users/yegle/Gentoo/bin/bash的,一般的mac系统应该是在/bin/bash。同时还要修改SSCL变量为你系统内sscl二进制文件路径,以及MOVIE_DIR你的电影文件所在路径。
  4. 在终端下执行sh sub.sh,即可自动匹配你的MOVIE_DIR目录下所有电影文件并自动将字幕复制到你的电影文件所在文件夹。如果有字幕没有下载到,将会在命令行里提示某个视频没有找到对应字幕。

脚本

#!/Users/yegle/Gentoo/bin/bash
MOVIE_DIR=/Users/yegle/Downloads/
MOVIE_LIST=$(mktemp)
SSCL=/Users/yegle/sscl
rm -rf "$HOME/Library/Application Support/SPlayerX/SVPSub/"*
find $MOVIE_DIR -regextype posix-extended -iregex '.*\.(mkv|avi)$'|grep -iv sample >$MOVIE_LIST
while read movie_file_path
do
    movie_filename=$(basename "$movie_file_path")
    movie_path=$(dirname "$movie_file_path")
    if [ ! -e "${movie_file_path%.*}.chn.srt" ]
    then
        $SSCL --video-file "$movie_file_path" --pull >/dev/null 2>&1
        ls "$HOME/Library/Application Support/SPlayerX/SVPSub/${movie_filename%.*}.chn.srt" >/dev/null 2>&1 \
            && mv "$HOME/Library/Application Support/SPlayerX/SVPSub/${movie_filename%.*}".*.srt "$movie_path" \
            || echo $movie_file_path subtitle not found
    fi
done < $MOVIE_LIST

2011-06-23更新:重写了一个脚本,功能相同,但是使用的是Mac自带的findmktemp,这样无论是否有GNU的findmktemp安装,都能正常执行了

#!/bin/bash

MOVIE_DIR="/Volumes/Macintosh HD/Movie"
MOVIE_LIST=$(/usr/bin/mktemp -t sub)
SSCL=/Users/yegle/sscl

rm -rf "$HOME/Library/Application Support/SPlayerX/SVPSub/"*
/usr/bin/find -E "${MOVIE_DIR}" -iregex '.*\.(avi|mkv)$' |grep -iv sample >$MOVIE_LIST

while read movie_file_path
do
    movie_filename=$(basename "$movie_file_path")
    movie_path=$(dirname "$movie_file_path")
    if [ ! -e "${movie_file_path%.*}.chn.srt" ]
    then
        $SSCL --video-file "$movie_file_path" --pull >/dev/null 2>&1
        ls "$HOME/Library/Application Support/SPlayerX/SVPSub/${movie_filename%.*}.chn.srt" >/dev/null 2>&1 \
            && mv "$HOME/Library/Application Support/SPlayerX/SVPSub/${movie_filename%.*}".*.srt "$movie_path" \
            || echo $movie_file_path subtitle not found
    fi
done < $MOVIE_LIST

2014-04-10更新:重写了一个Python版本,功能相同但是使用Python内置工具,应该比shell脚本方便

#!/usr/bin/env python
# vim: set fileencoding=utf-8 ts=4 sw=4 tw=79 :

from __future__ import (unicode_literals, absolute_import,
                        division, print_function)

import os
import subprocess
import shutil
from glob import glob

from os.path import exists, join, basename

movie_dir = os.path.expanduser('~/Movie/')
sscl = os.path.expanduser('~/sscl')
base_args = [sscl, '--pull', '--video-file']

if __name__ == '__main__':
    for root, dirs, files in os.walk(movie_dir):
        for f in files:
            f_wo_ext, ext = os.path.splitext(f)
            if ext not in {'.mkv'} or 'sample' in f_wo_ext.lower():
                continue
            filename_wo_ext = join(root, f_wo_ext)

            if (glob("%s*.srt" % filename_wo_ext)
                or glob("%s*.ass" % filename_wo_ext)):
                continue

            filename = join(root, f)
            try:
                result = subprocess.check_output(
                    base_args + [filename], timeout=10)
                subs = set(result.decode('utf-8').strip().split('\n'))
                for sub in subs:
                    if not exists(join(root, basename(sub))):
                        shutil.move(sub, root)
            except subprocess.CalledProcessError as e:
                print("NOT FOUND: %s" % f)
            except subprocess.TimeoutExpired:
                print("TIMEOUT: %s" % f)

Comments !

blogroll

social