mirror of https://github.com/alexta69/metube
commit
d1cbf748f9
|
@ -10,9 +10,12 @@ FROM python:3.8-alpine
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY Pipfile* docker-entrypoint.sh ./
|
COPY Pipfile* docker-entrypoint.sh .
|
||||||
|
|
||||||
RUN chmod +x docker-entrypoint.sh && \
|
# Use sed to strip carriage-return characters from the entrypoint script (in case building on Windows)
|
||||||
|
# Install dependencies
|
||||||
|
RUN sed -i 's/\r$//g' docker-entrypoint.sh && \
|
||||||
|
chmod +x docker-entrypoint.sh && \
|
||||||
apk add --update ffmpeg aria2 coreutils shadow su-exec && \
|
apk add --update ffmpeg aria2 coreutils shadow su-exec && \
|
||||||
apk add --update --virtual .build-deps gcc g++ musl-dev && \
|
apk add --update --virtual .build-deps gcc g++ musl-dev && \
|
||||||
pip install --no-cache-dir pipenv && \
|
pip install --no-cache-dir pipenv && \
|
||||||
|
@ -32,6 +35,7 @@ ENV UMASK=022
|
||||||
|
|
||||||
ENV DOWNLOAD_DIR /downloads
|
ENV DOWNLOAD_DIR /downloads
|
||||||
ENV STATE_DIR /downloads/.metube
|
ENV STATE_DIR /downloads/.metube
|
||||||
|
ENV TEMP_DIR /downloads
|
||||||
VOLUME /downloads
|
VOLUME /downloads
|
||||||
EXPOSE 8081
|
EXPOSE 8081
|
||||||
CMD [ "./docker-entrypoint.sh" ]
|
CMD [ "./docker-entrypoint.sh" ]
|
||||||
|
|
16
README.md
16
README.md
|
@ -3,7 +3,7 @@
|
||||||
![Build Status](https://github.com/alexta69/metube/actions/workflows/main.yml/badge.svg)
|
![Build Status](https://github.com/alexta69/metube/actions/workflows/main.yml/badge.svg)
|
||||||
![Docker Pulls](https://img.shields.io/docker/pulls/alexta69/metube.svg)
|
![Docker Pulls](https://img.shields.io/docker/pulls/alexta69/metube.svg)
|
||||||
|
|
||||||
Web GUI for youtube-dl (using the [yt-dlp](https://github.com/yt-dlp/yt-dlp) fork) with playlist support. Allows you to download videos from YouTube and dozens of other sites (https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md).
|
Web GUI for youtube-dl (using the [yt-dlp](https://github.com/yt-dlp/yt-dlp) fork) with playlist support. Allows you to download videos from YouTube and [dozens of other sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md).
|
||||||
|
|
||||||
![screenshot1](https://github.com/alexta69/metube/raw/master/screenshot.gif)
|
![screenshot1](https://github.com/alexta69/metube/raw/master/screenshot.gif)
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ Web GUI for youtube-dl (using the [yt-dlp](https://github.com/yt-dlp/yt-dlp) for
|
||||||
```bash
|
```bash
|
||||||
docker run -d -p 8081:8081 -v /path/to/downloads:/downloads ghcr.io/alexta69/metube
|
docker run -d -p 8081:8081 -v /path/to/downloads:/downloads ghcr.io/alexta69/metube
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run using docker-compose
|
## Run using docker-compose
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -40,6 +41,9 @@ Certain values can be set via environment variables, using the `-e` parameter on
|
||||||
* __CUSTOM_DIRS__: whether to enable downloading videos into custom directories within the __DOWNLOAD_DIR__ (or __AUDIO_DOWNLOAD_DIR__). When enabled, a drop-down appears next to the Add button to specify the download directory. Defaults to `true`.
|
* __CUSTOM_DIRS__: whether to enable downloading videos into custom directories within the __DOWNLOAD_DIR__ (or __AUDIO_DOWNLOAD_DIR__). When enabled, a drop-down appears next to the Add button to specify the download directory. Defaults to `true`.
|
||||||
* __CREATE_CUSTOM_DIRS__: whether to support automatically creating directories within the __DOWNLOAD_DIR__ (or __AUDIO_DOWNLOAD_DIR__) if they do not exist. When enabled, the download directory selector becomes supports free-text input, and the specified directory will be created recursively. Defaults to `true`.
|
* __CREATE_CUSTOM_DIRS__: whether to support automatically creating directories within the __DOWNLOAD_DIR__ (or __AUDIO_DOWNLOAD_DIR__) if they do not exist. When enabled, the download directory selector becomes supports free-text input, and the specified directory will be created recursively. Defaults to `true`.
|
||||||
* __STATE_DIR__: path to where the queue persistence files will be saved. Defaults to `/downloads/.metube` in the docker image, and `.` otherwise.
|
* __STATE_DIR__: path to where the queue persistence files will be saved. Defaults to `/downloads/.metube` in the docker image, and `.` otherwise.
|
||||||
|
* __TEMP_DIR__: path where intermediary download files will be saved. Defaults to `/downloads` in the docker image, and `.` otherwise.
|
||||||
|
* Set this to an SSD or RAM filesystem (e.g., `tmpfs`) for better performance
|
||||||
|
* __Note__: Using a RAM filesystem may prevent downloads from being resumed
|
||||||
* __DELETE_FILE_ON_TRASHCAN__: if `true`, downloaded files are deleted on the server, when they are trashed from the "Completed" section of the UI. Defaults to `false`.
|
* __DELETE_FILE_ON_TRASHCAN__: if `true`, downloaded files are deleted on the server, when they are trashed from the "Completed" section of the UI. Defaults to `false`.
|
||||||
* __URL_PREFIX__: base path for the web server (for use when hosting behind a reverse proxy). Defaults to `/`.
|
* __URL_PREFIX__: base path for the web server (for use when hosting behind a reverse proxy). Defaults to `/`.
|
||||||
* __OUTPUT_TEMPLATE__: the template for the filenames of the downloaded videos, formatted according to [this spec](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#output-template). Defaults to `%(title)s.%(ext)s`.
|
* __OUTPUT_TEMPLATE__: the template for the filenames of the downloaded videos, formatted according to [this spec](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#output-template). Defaults to `%(title)s.%(ext)s`.
|
||||||
|
@ -47,11 +51,13 @@ Certain values can be set via environment variables, using the `-e` parameter on
|
||||||
* __YTDL_OPTIONS__: Additional options to pass to youtube-dl, in JSON format. [See available options here](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L183). They roughly correspond to command-line options, though some do not have exact equivalents here, for example `--recode-video` has to be specified via `postprocessors`. Also note that dashes are replaced with underscores.
|
* __YTDL_OPTIONS__: Additional options to pass to youtube-dl, in JSON format. [See available options here](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L183). They roughly correspond to command-line options, though some do not have exact equivalents here, for example `--recode-video` has to be specified via `postprocessors`. Also note that dashes are replaced with underscores.
|
||||||
|
|
||||||
The following example value for `YTDL_OPTIONS` embeds English subtitles and chapter markers (for videos that have them), and also changes the permissions on the downloaded video:
|
The following example value for `YTDL_OPTIONS` embeds English subtitles and chapter markers (for videos that have them), and also changes the permissions on the downloaded video:
|
||||||
```
|
|
||||||
|
```json
|
||||||
{"writesubtitles": true, "subtitleslangs": ["en", "-live_chat"], "postprocessors": [{"key": "Exec", "exec_cmd": "chmod 0664", "when": "after_move"}, {"key": "FFmpegEmbedSubtitle", "already_have_subtitle": false}, {"key": "FFmpegMetadata", "add_chapters": true}]}
|
{"writesubtitles": true, "subtitleslangs": ["en", "-live_chat"], "postprocessors": [{"key": "Exec", "exec_cmd": "chmod 0664", "when": "after_move"}, {"key": "FFmpegEmbedSubtitle", "already_have_subtitle": false}, {"key": "FFmpegMetadata", "add_chapters": true}]}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using browser cookies
|
## Using browser cookies
|
||||||
|
|
||||||
In case you need to use your browser's cookies with MeTube, for example to download restricted or private videos:
|
In case you need to use your browser's cookies with MeTube, for example to download restricted or private videos:
|
||||||
|
|
||||||
* Add the following to your docker-compose.yml:
|
* Add the following to your docker-compose.yml:
|
||||||
|
@ -62,9 +68,10 @@ In case you need to use your browser's cookies with MeTube, for example to downl
|
||||||
environment:
|
environment:
|
||||||
- YTDL_OPTIONS={"cookiefile":"/cookies/cookies.txt"}
|
- YTDL_OPTIONS={"cookiefile":"/cookies/cookies.txt"}
|
||||||
```
|
```
|
||||||
|
|
||||||
* Install in your browser an extension to extract cookies:
|
* Install in your browser an extension to extract cookies:
|
||||||
* [Firefox](https://addons.mozilla.org/en-US/firefox/addon/export-cookies-txt/)
|
* [Firefox](https://addons.mozilla.org/en-US/firefox/addon/export-cookies-txt/)
|
||||||
* [Chrome](https://chrome.google.com/webstore/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc)
|
* [Chrome](https://chrome.google.com/webstore/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc)
|
||||||
* Extract the cookies you need with the extension and rename the file `cookies.txt`
|
* Extract the cookies you need with the extension and rename the file `cookies.txt`
|
||||||
* Drop the file in the folder you configured in the docker-compose.yml above
|
* Drop the file in the folder you configured in the docker-compose.yml above
|
||||||
* Restart the container
|
* Restart the container
|
||||||
|
@ -88,6 +95,7 @@ javascript:!function(){xhr=new XMLHttpRequest();xhr.open("POST","https://metube.
|
||||||
```
|
```
|
||||||
|
|
||||||
[shoonya75](https://github.com/shoonya75) has contributed a Firefox version:
|
[shoonya75](https://github.com/shoonya75) has contributed a Firefox version:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
javascript:(function(){xhr=new XMLHttpRequest();xhr.open("POST","https://metube.domain.com/add");xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function(){if(xhr.status==200){alert("Sent to metube!")}else{alert("Send to metube failed. Check the javascript console for clues.")}}})();
|
javascript:(function(){xhr=new XMLHttpRequest();xhr.open("POST","https://metube.domain.com/add");xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function(){if(xhr.status==200){alert("Sent to metube!")}else{alert("Send to metube failed. Check the javascript console for clues.")}}})();
|
||||||
```
|
```
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Config:
|
||||||
_DEFAULTS = {
|
_DEFAULTS = {
|
||||||
'DOWNLOAD_DIR': '.',
|
'DOWNLOAD_DIR': '.',
|
||||||
'AUDIO_DOWNLOAD_DIR': '%%DOWNLOAD_DIR',
|
'AUDIO_DOWNLOAD_DIR': '%%DOWNLOAD_DIR',
|
||||||
|
'TEMP_DIR': '%%DOWNLOAD_DIR',
|
||||||
'DOWNLOAD_DIRS_INDEXABLE': 'false',
|
'DOWNLOAD_DIRS_INDEXABLE': 'false',
|
||||||
'CUSTOM_DIRS': 'true',
|
'CUSTOM_DIRS': 'true',
|
||||||
'CREATE_CUSTOM_DIRS': 'true',
|
'CREATE_CUSTOM_DIRS': 'true',
|
||||||
|
@ -192,4 +193,5 @@ app.on_response_prepare.append(on_prepare)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
log.info(f"Listening on {config.HOST}:{config.PORT}")
|
||||||
web.run_app(app, host=config.HOST, port=config.PORT, reuse_port=True)
|
web.run_app(app, host=config.HOST, port=config.PORT, reuse_port=True)
|
||||||
|
|
|
@ -42,8 +42,9 @@ class DownloadInfo:
|
||||||
class Download:
|
class Download:
|
||||||
manager = None
|
manager = None
|
||||||
|
|
||||||
def __init__(self, download_dir, output_template, output_template_chapter, quality, format, ytdl_opts, info):
|
def __init__(self, download_dir, temp_dir, output_template, output_template_chapter, quality, format, ytdl_opts, info):
|
||||||
self.download_dir = download_dir
|
self.download_dir = download_dir
|
||||||
|
self.temp_dir = temp_dir
|
||||||
self.output_template = output_template
|
self.output_template = output_template
|
||||||
self.output_template_chapter = output_template_chapter
|
self.output_template_chapter = output_template_chapter
|
||||||
self.format = get_format(format, quality)
|
self.format = get_format(format, quality)
|
||||||
|
@ -81,7 +82,7 @@ class Download:
|
||||||
'quiet': True,
|
'quiet': True,
|
||||||
'no_color': True,
|
'no_color': True,
|
||||||
#'skip_download': True,
|
#'skip_download': True,
|
||||||
'paths': {"home": self.download_dir},
|
'paths': {"home": self.download_dir, "temp": self.temp_dir},
|
||||||
'outtmpl': { "default": self.output_template, "chapter": self.output_template_chapter },
|
'outtmpl': { "default": self.output_template, "chapter": self.output_template_chapter },
|
||||||
'format': self.format,
|
'format': self.format,
|
||||||
'socket_timeout': 30,
|
'socket_timeout': 30,
|
||||||
|
@ -159,7 +160,7 @@ class PersistentQueue:
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
for k, v in self.saved_items():
|
for k, v in self.saved_items():
|
||||||
self.dict[k] = Download(None, None, None, None, None, {}, v)
|
self.dict[k] = Download(None, None, None, None, None, None, {}, v)
|
||||||
|
|
||||||
def exists(self, key):
|
def exists(self, key):
|
||||||
return key in self.dict
|
return key in self.dict
|
||||||
|
@ -258,7 +259,7 @@ class DownloadQueue:
|
||||||
for property, value in entry.items():
|
for property, value in entry.items():
|
||||||
if property.startswith("playlist"):
|
if property.startswith("playlist"):
|
||||||
output = output.replace(f"%({property})s", str(value))
|
output = output.replace(f"%({property})s", str(value))
|
||||||
self.queue.put(Download(dldirectory, output, output_chapter, quality, format, self.config.YTDL_OPTIONS, dl))
|
self.queue.put(Download(dldirectory, self.config.TEMP_DIR, output, output_chapter, quality, format, self.config.YTDL_OPTIONS, dl))
|
||||||
self.event.set()
|
self.event.set()
|
||||||
await self.notifier.added(dl)
|
await self.notifier.added(dl)
|
||||||
return {'status': 'ok'}
|
return {'status': 'ok'}
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
echo "Setting umask to ${UMASK}"
|
echo "Setting umask to ${UMASK}"
|
||||||
umask ${UMASK}
|
umask ${UMASK}
|
||||||
echo "Creating download directory ${DOWNLOAD_DIR} and state directory ${STATE_DIR}"
|
echo "Creating download directory (${DOWNLOAD_DIR}), state directory (${STATE_DIR}), and temp dir (${TEMP_DIR})"
|
||||||
mkdir -p "${DOWNLOAD_DIR}" "${STATE_DIR}"
|
mkdir -p "${DOWNLOAD_DIR}" "${STATE_DIR}" "${TEMP_DIR}"
|
||||||
|
|
||||||
if [ `id -u` -eq 0 ] && [ `id -g` -eq 0 ]; then
|
if [ `id -u` -eq 0 ] && [ `id -g` -eq 0 ]; then
|
||||||
if [ "${UID}" -eq 0 ]; then
|
if [ "${UID}" -eq 0 ]; then
|
||||||
echo "Warning: it is not recommended to run as root user, please check your setting of the UID environment variable"
|
echo "Warning: it is not recommended to run as root user, please check your setting of the UID environment variable"
|
||||||
fi
|
fi
|
||||||
echo "Changing ownership of download and state directories to ${UID}:${GID}"
|
echo "Changing ownership of download and state directories to ${UID}:${GID}"
|
||||||
chown -R "${UID}":"${GID}" /app "${DOWNLOAD_DIR}" "${STATE_DIR}"
|
chown -R "${UID}":"${GID}" /app "${DOWNLOAD_DIR}" "${STATE_DIR}" "${TEMP_DIR}"
|
||||||
echo "Running MeTube as user ${UID}:${GID}"
|
echo "Running MeTube as user ${UID}:${GID}"
|
||||||
su-exec "${UID}":"${GID}" python3 app/main.py
|
su-exec "${UID}":"${GID}" python3 app/main.py
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue