最近在研究如何把youtube-dl跑在Android上(youtube-dl#967)。方法大致如下:
- 找一個可以跑的Python interpreter
- 把youtube-dl clone下來或是從yt-dl.org下載最新的tarball
- 用adb shell或terminal emulater執行youtube-dl
針對第一步,Google Play上面很多個,例如Kivy Launcher,QPython3等等。我玩過之後都不太滿意。因為這些App要嘛我找不到一個單獨的python執行檔,要嘛是在app自己的資料夾下,shell讀不到。於是我決定用自己compile的版本。在網路上找"Python NDK",找到了python3-android這個project。照著說明下去跑,沒有compile error,看起來很棒。放到手機上,設定好LD_LIBRARY_PATH
就可以用,也不錯,不過一跑youtube-dl就出錯:
root@GT-N7000:/data/local/tmp # python3 youtube_dl/__main__.py
Traceback (most recent call last):
File "youtube_dl/__main__.py", line 16, in <module>
import youtube_dl
File "/data/local/tmp/youtube_dl/__init__.py", line 15, in <module>
from .options import (
File "/data/local/tmp/youtube_dl/options.py", line 7, in <module>
from .downloader.external import list_external_downloaders
File "/data/local/tmp/youtube_dl/downloader/__init__.py", line 3, in <module>
from .common import FileDownloader
File "/data/local/tmp/youtube_dl/downloader/common.py", line 8, in <module>
from ..compat import compat_str
File "/data/local/tmp/youtube_dl/compat.py", line 14, in <module>
import subprocess
File "/data/local/tmp/python3/lib/python3.4/subprocess.py", line 406, in <module>
import select
ImportError: dlopen failed: cannot locate symbol "ceil" referenced by "select.cpython-34m.so"...
在另外一台手機HTC butterfly x920上error不太一樣:
shell@android:/data/local/tmp $ python3 -c 'import select'
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: Cannot load library: reloc_library[1306]: 2142 cannot locate 'ceil'...
在網路上找到這篇,他說select需要link libm.so
,我照做了之後select就可以被import進來了。
接下來是這個問題:
shell@android:/ $ python3 -c 'import platform; platform.platform()'
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/data/local/tmp/python3/lib/python3.4/platform.py", line 1470, in platform
system, node, release, version, machine, processor = uname()
File "/data/local/tmp/python3/lib/python3.4/platform.py", line 1151, in uname
processor = _syscmd_uname('-p', '')
File "/data/local/tmp/python3/lib/python3.4/platform.py", line 905, in _syscmd_uname
f = os.popen('uname %s 2> %s' % (option, DEV_NULL))
File "/data/local/tmp/python3/lib/python3.4/os.py", line 943, in popen
bufsize=buffering)
File "/data/local/tmp/python3/lib/python3.4/subprocess.py", line 859, in __init__
restore_signals, start_new_session)
File "/data/local/tmp/python3/lib/python3.4/subprocess.py", line 1357, in _execute_child
raise RuntimeError('Could not find system shell')
RuntimeError: Could not find system shell
看了下subprocess.py
,這段code是python3-android的patch:
import platform
if platform.android_version()[0]:
main = '/system/bin/sh'
else:
raise RuntimeError('Could not find system shell')
在蝴蝶機上platform.android_version()
給出的是('', '')
,於是我又看了下android_version()
的定義:
output = subprocess.check_output(['/system/bin/getprop',
_android_version_property])
version = output.decode('ascii').strip()
version_obtained = True
奇怪的是,我在shell裡直接打/system/bin/getprop ro.build.version.release
就會有正確的值4.1.1,用python下subprocess.check_output(['/system/bin/getprop', 'ro.build.version.release'])
或是subprocess.check_output(['/system/bin/getprop'])
都給我b''
。strace一下也沒啥發現。只好來改falling back讀檔案的部份。
不得不說這部份真是bug百出,原作者應該沒有好好的在各種手機上測過,最後改完大概像這個樣子。
這兩個地方我把改過的部份送回上游python3-android#10,pull request裡也有說明我遇到的各種情況。
現在大致上可以跑了,除了SSL CERTIFICATE_VERIFY_FAILED
之外:
shell@android:/sdcard/Android/data/me.sheimi.sgit/files/repo/ytdl $ python3 -m youtube_dl -v cnFmi7AXeK8
[debug] System config: []
[debug] User config: []
[debug] Command-line args: ['-v', 'cnFmi7AXeK8']
[debug] Encodings: locale ascii, fs utf-8, out ascii, pref ascii
[debug] youtube-dl version 2015.11.19
[debug] Python version 3.4.3 - Linux-3.4.10-ga9fe3b3-armv7l-with-libc
[debug] exe versions: none
[debug] Proxy map: {}
[youtube] cnFmi7AXeK8: Downloading webpage
ERROR: Unable to download webpage: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)> (caused by URLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)'),))
response = self._open(req, data)
File "/storage/sdcard0/Android/data/me.sheimi.sgit/files/repo/ytdl/youtube_dl/extractor/common.py", line 329, in _request_webpage
return self._downloader.urlopen(url_or_request)
File "/storage/sdcard0/Android/data/me.sheimi.sgit/files/repo/ytdl/youtube_dl/YoutubeDL.py", line 1874, in urlopen
return self._opener.open(req, timeout=self._socket_timeout)
File "/data/local/tmp/python3/lib/python3.4/urllib/request.py", line 463, in open
File "/data/local/tmp/python3/lib/python3.4/urllib/request.py", line 481, in _open
'_open', req)
File "/data/local/tmp/python3/lib/python3.4/urllib/request.py", line 441, in _call_chain
result = func(*args)
File "/storage/sdcard0/Android/data/me.sheimi.sgit/files/repo/ytdl/youtube_dl/utils.py", line 798, in https_open
req, **kwargs)
File "/data/local/tmp/python3/lib/python3.4/urllib/request.py", line 1184, in do_open
raise URLError(err)
這是個老問題了youtube-dl ssl certificate_verify_failed,一個常見的解法是設定$SSL_CERT_DIR
,但是我設了/system/etc/security/cacerts
還是失敗,strace一下:
stat64("/system/etc/security/cacerts/578d5c04.0", 0xbe99c130) = -1 ENOENT (No such file or directory)
stat64("/system/etc/security/cacerts/2c543cd1.0", 0xbe99c130) = -1 ENOENT (No such file or directory)
stat64("/system/etc/security/cacerts/c4c7a654.0", 0xbe99c130) = -1 ENOENT (No such file or directory)
Android的確沒有這三個檔案,而Arch Linux上有2c543cd1.0和578d5c04.0,莫非Android上的根憑證和一般人不一樣?
EDIT 2019-09-21
後來certificate的問題解決了。詳細可參考python3-android SSL/TLS。