Back in the days when the command line utility youtube-dl still worked, I was quite impressed that:
- The download was a single executable file (
youtube-dlfor Linux,youtube-dl.exefor Windows) - If you ran the update command,
youtube-dl -U- then if there was an update, the actual single file got "replaced", so the update worked even if you did not have the program "installed", and you were running it from an arbitrary user directory instead.
I would like to implement something similar, but I have a difficulty gathering the minimal requirements for how to setup the software, and how to setup the web server side - e.g. if my main update URL is https://example.com/mysoft/update/, what files would I need to have there, so the entire mechanism works?
Here is what I can see so far:
- If you download the Linux version of youtube-dl, this is what I get when I run
fileon it (in MINGW64 console):
$ file youtube-dl
youtube-dl: a /usr/bin/env python script executable (Zip archive)
So, it is somehow an "executable zip" file, not sure how that was made; inside it we can see:
$ unzip -l youtube-dl
Archive: youtube-dl
warning [youtube-dl]: 22 extra bytes at beginning or within zipfile
(attempting to process anyway)
Length Date Time Name
--------- ---------- ----- ----
16130 1999-12-31 19:01 youtube_dl/aes.py
2981 1999-12-31 19:01 youtube_dl/cache.py
94259 1999-12-31 19:01 youtube_dl/compat.py
20585 1999-12-31 19:01 youtube_dl/__init__.py
...
955 1999-12-31 19:01 youtube_dl/postprocessor/__init__.py
1652 1999-12-31 19:01 youtube_dl/postprocessor/metadatafromtitle.py
2926 1999-12-31 19:01 youtube_dl/postprocessor/xattrpp.py
467 1999-12-31 19:01 __main__.py
--------- -------
5436380 818 files
So apparently:
- There has to be a
__main__.pyin the zip (is the__main__.pyname required or arbitrary? - Rest of the software needs to be a Python module, in its own (sub)directory (
youtube-dl/), which has an__init__.pyinside it
In the Makefile, I can see a youtube-dl target, which apparently clarifies how the final "executable zip" is created:
youtube-dl: youtube_dl/*.py youtube_dl/*/*.py
mkdir -p zip
for d in youtube_dl youtube_dl/downloader youtube_dl/extractor youtube_dl/postprocessor ; do \
mkdir -p zip/$$d ;\
cp -pPR $$d/*.py zip/$$d/ ;\
done
touch -t 200001010101 zip/youtube_dl/*.py zip/youtube_dl/*/*.py
mv zip/youtube_dl/__main__.py zip/
cd zip ; zip -q ../youtube-dl youtube_dl/*.py youtube_dl/*/*.py __main__.py
rm -rf zip
echo '#!$(PYTHON)' > youtube-dl
cat youtube-dl.zip >> youtube-dl
rm youtube-dl.zip
chmod a+x youtube-dl
... however, I cannot see in the Makefile how the Windows .exe is created.
However, in setup.py I can see:
# ..
py2exe_console = [{
'script': './youtube_dl/__main__.py',
'dest_base': 'youtube-dl',
'version': __version__,
'description': DESCRIPTION,
'comments': LONG_DESCRIPTION,
'product_name': 'youtube-dl',
'product_version': __version__,
}]
py2exe_params = {
'console': py2exe_console,
'options': {'py2exe': py2exe_options},
'zipfile': None
}
# ...
... so apparently somehow py2exe is used for the Windows .exe.
If the Makefile does not directly handle the .exe, I guess then devscripts/wine-py2exe.sh does - but I cannot tell how (if at all) it is integrated in the build process.
Handling the update from within the program is probably handled by youtube_dl/update.py, where I can see:
# ...
def update_self(to_screen, verbose, opener):
"""Update the program file with the latest version from the repository"""
UPDATE_URL = 'https://yt-dl.org/update/'
VERSION_URL = UPDATE_URL + 'LATEST_VERSION'
JSON_URL = UPDATE_URL + 'versions.json'
UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537)
if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, 'frozen'):
to_screen('It looks like you installed youtube-dl with a package manager, pip, setup.py or a tarball. Please use that to update.')
return
# Check if there is a new version
try:
newversion = opener.open(VERSION_URL).read().decode('utf-8').strip()
# ...
So, on the web/online side, I need at least a UPDATE_URL/LATEST_VERSION and UPDATE_URL/versions.json; where versions.json looks something like:
{
"latest": "2013.01.06",
"signature": "72158cdba391628569ffdbea259afbcf279bbe3d8aeb7492690735dc1cfa6afa754f55c61196f3871d429599ab22f2667f1fec98865527b32632e7f4b3675a7ef0f0fbe084d359256ae4bba68f0d33854e531a70754712f244be71d4b92e664302aa99653ee4df19800d955b6c4149cd2b3f24288d6e4b40b16126e01f4c8ce6",
"versions": {
"2013.01.02": {
...
},
"2013.01.06": {
"bin": [
"http://youtube-dl.org/downloads/2013.01.06/youtube-dl",
"64b6ed8865735c6302e836d4d832577321b4519aa02640dc508580c1ee824049"
],
"exe": [
"http://youtube-dl.org/downloads/2013.01.06/youtube-dl.exe",
"58609baf91e4389d36e3ba586e21dab882daaaee537e4448b1265392ae86ff84"
],
"tar": [
"http://youtube-dl.org/downloads/2013.01.06/youtube-dl-2013.01.06.tar.gz",
"fe77ab20a95d980ed17a659aa67e371fdd4d656d19c4c7950e7b720b0c2f1a86"
]
}
...
So, there are separate downloads for the Linux "executable Python zip", and for the Windows .exe file; there seem to be file hashes as well.
Ultimately, on one hand I'd like to avoid reinventing the wheel for the update procedure; on the other hand, I'd like to also avoid learning the youtube-dl code in enough detail, to extract the update procedure for my program. So my questions are:
- Is there a name for a technique for a similar cross-platform update technique for Python programs, so I can (hopefully) find a simple bare-bones example, that I could build upon?
- Lacking that, can someone with more experience in this summarize what is necessary (e.g. organization in modules, what the build scripts need to do, what the web server layout should) to implement such an update mechanism?
- Finally - if I want to update a GUI program, so it "restarts" itself in case there was an update, how would one go about that? (note in the command line
-Uprocedure, the software overwrites the single executable file (zip or exe), and then exits - it does not "restart" after the update is done).