First commit

Jerry Aldrich 2 years ago
commit 4c814ab48a
  1. 1
      .dockerignore
  2. 1
      .gitignore
  3. 23
      Dockerfile
  4. 94
      README.md
  5. 21
      compose.yaml
  6. 168
      entrypoint.sh
  7. 23
      init-data-dir.sh

@ -0,0 +1 @@
jellyfin/*

1
.gitignore vendored

@ -0,0 +1 @@
data/

@ -0,0 +1,23 @@
FROM python:3.9.17-slim
# https://pypi.org/project/yt-dlp/
ARG YT_DLP_VERSION=2023.6.22
RUN groupadd -g 1337 python && \
useradd -r -u 1337 -g python --home-dir /srv python
RUN chown -R python:python /srv
RUN mkdir /export && chown python:python /export
COPY entrypoint.sh /srv/entrypoint.sh
RUN chown python:python /srv/entrypoint.sh
RUN chmod +x /srv/entrypoint.sh
USER python
WORKDIR /srv/youtube-dlp
RUN pip install --user --upgrade yt-dlp==${YT_DLP_VERSION}
ENTRYPOINT ["/srv/entrypoint.sh", "/export/batch.txt"]

@ -0,0 +1,94 @@
# Pirate YouTube
> WARNING: This repo only supports Linux at this time
Do you have an ever growing list of "Watch Later" videos on YouTube?
Do you also have a significant amount of time coming up where you won't have
access to the Internet? Like, say, a flight to Taiwan?
This repo has a solution!
Simply:
- Create a `batch.txt` file with URls to YouTube videos
- Run `docker compose up`
- View your videos at [http://localhost:8080](http://localhost:8080)
- ...PROFIT!!!
Okay, it's not *quite* that simple. See [Usage](#Usage) for actual steps.
## Usage
Clone this repo:
- Run the following:
```
git clone git@git.jerryaldrichiii.com:jerry/pirate-youtube.git
```
Create data directory:
- Run the following (using `sudo` or root):
```
sudo sh init-data-dir.sh
```
Populate 'batch.txt':
- Using `echo` (Or...realistically a editor...with sudo/root):
```
echo "https://www.youtube.com/watch?v=o-YBDTqX_ZU" | sudo tee -a "data/batch.txt"
```
> If you don't use an editor/sudo/root, then run the following:
> `chown -R 1337 data/batch.txt`
### Download YouTube Vidoes
Start `yt-dlp` container:
```
sudo docker compose up ytdlp
```
### Start/Configure Jellyfin
Start Jellyfin:
```
sudo docker compose up jellyfin
```
Configure Jellyfin:
- Connect to [http://localhost:8080](http://localhost:8080)
- Next -> Add User -> Add Media Library
- Content type -> Mixed Movies and Shows
- Display name: Pirate YouTube
- Click `+` on Folders
- Add /media/pirate-youtube
- Click `Ok`
- Click `Ok` (again)
- Click `Next`, `Next`, `Next` (Your choice on "Remote Access")
- Click `Finish`
- "Sign In"
- Click hamburger menu in top-left (three horizontal bars)
- Administration -> Dashboard -> Scan All Libraries
- Click the "House" icon
- Profit!!!
## FAQ
### Why not youtube-dl?
At the time of this writing, the last release of youtube-dl was 2023/12/17
which was non-functional with current YouTube URLs
### How do I add more videos?
- Add URLs to `data/batch.txt` (make sure you use `sudo` or root)
- Run `docker compose up ytdlp` in an another terminal
- In Jellyfin ([http://localhost:8080](http://localhost:8080)):
- Login as admin, then goto the "Dashboard" and "Scan All Libraries"
### How do I remove videos?
- Remove relevant commented URL in `data/batch.txt` (NOTE: use `sudo` or root)
- In Jellyfin ([http://localhost:8080](http://localhost:8080)):
- Login as admin, goto "My Media", then "Pirate YouTube", and "Delete Media"

@ -0,0 +1,21 @@
version: '3.5'
services:
ytdlp:
build: .
container_name: yt-dlp
volumes:
- ./data/jellyfin/media/pirate-youtube:/export/jellyfin
- ./data/batch.txt:/export/batch.txt
- ./entrypoint.sh:/srv/entrypoint.sh
environment:
- LOG_LEVEL=DEBUG
jellyfin:
image: jellyfin/jellyfin
container_name: jellyfin
user: 1337:1337
volumes:
- ./data/jellyfin/config:/config
- ./data/jellyfin/cache:/cache
- ./data/jellyfin/media:/media
ports:
- 8080:8096

@ -0,0 +1,168 @@
#!/usr/bin/env bash
# Exit 1 if any command errors
set -e
# $1 here is the first argument to this script
BATCH_FILE="$1"
DOWNLOAD_DIR="/export/jellyfin"
YTDLP_LOG_FILE="/export/ytdlp.log"
# 'cat << 'EOF"' is used here to allow for newlines in the command
YTDLP_COMMAND=$(cat << EOF
/srv/.local/bin/yt-dlp
-P $DOWNLOAD_DIR
--sub-format srt
--write-subs
--write-thumbnail
EOF
)
printUsage() {
cat << EOF
Usage: $(basename "$0") <batch.txt>
EOF
}
# log() takes a log level and log line and uses the LOG_LEVEL env var to
# control how/when it is logged
# To enable debug logging set LOG_LEVEL=debug as an env var
log() {
# '$1' is the first argument passed to this function
passed_level="$1"
# '$2' is the first argument passed to this function
log_line="$2"
# This is passed from the env as 'LOG_LEVEL='
env_level="$LOG_LEVEL"
# Convert env_level to int
case $env_level in
"DEBUG")
min_level_int=0;;
"INFO")
min_level_int=1;;
"ERROR")
min_level_int=2;;
*)
min_level_int=1;;
esac
# Convert passed_level to int
case $passed_level in
"DEBUG")
log_level_int=0;;
"INFO")
log_level_int=1;;
"ERROR")
log_level_int=1;;
*)
log_level_int=99;;
esac
if [ $log_level_int -ge $min_level_int ]; then
echo "[$passed_level] $log_line"
fi
}
validate_args() {
# If no argument specified, exit 1
if [ -z "$BATCH_FILE" ]; then
printUsage
exit 1
fi
# Exit 1 if passed batch file is not a file (or doesn't exist)
if [ ! -f "$BATCH_FILE" ]; then
echo "Error: Batch file '$BATCH_FILE' not found"
printUsage
exit 1
fi
# Exit 0 if passed file contains no urls
if ! grep -q "^.*http.*" "$BATCH_FILE"; then
echo "File '$BATCH_FILE' contains no URLs"
exit 1
fi
if [[ ! -O "$BATCH_FILE" ]]; then
echo "File '$BATCH_FILE' is not owned by user ID '${UID}'"
echo "Run 'chown ${UID} $BATCH_FILE' before mounting"
exit 1
fi
if [[ ! -O "$DOWNLOAD_DIR" ]]; then
echo "Directory '$DOWNLOAD_DIR' is not owned by user ID '${UID}'"
echo "Run 'chown -R ${UID} $DOWNLOAD_DIR' before mounting"
exit 1
fi
}
process_batchfile() {
# Using `read` parse each line of the file as a '$line' while setting IFS=''
# to prevent lines containing things such as `\n` from being parsed as a
# literal newline
while IFS='' read -r line; do
# Ignore any commented lines RegEx explanation: https://regexr.com/7hs52
regex_match_leading_comment='^\s*[#].*$'
if [[ "$line" =~ $regex_match_leading_comment ]]; then
# RegEx explanation: https://regexr.com/7i073
line_without_comment=$(echo "$line" | sed 's/^\s*[#]\s*\(.*\)$/\1/')
log "INFO" "Skipping: $line_without_comment"
continue
fi
log "INFO" "Downloading: $line"
# Temporarily allow commands to fail without exiting
set +e
# Run the command with $line as an argument
# Quotes are used here as a measure to protect against "\n" and the like
# '>' is used to overwrite the log file each call so only the last commands
# output is shown
$YTDLP_COMMAND "$line" > $YTDLP_LOG_FILE 2>&1
# Save the exit code of the last command
exit_code="$?"
# Re-enable exiting 1 on command failure
set -e
if [ "$exit_code" -ne "0" ]; then
log "ERROR" "Command failed: '$YTDLP_COMMAND'"
cat $YTDLP_LOG_FILE
continue
fi
log "INFO" "Finished downloading '$line'"
# By here we can assume the file was downloaded successfully, thus we can
# comment out the line in the file
log "DEBUG" "Commenting out '$line' in '$BATCH_FILE'"
# Prepend a '#" on the current line in the file
# '|' is used because URLs contain '/' which is the usual sed delimiter
# 'cp' to a buffer file is used because sed cannot modify the mounted
# file's inode when using Docker ('-c' is not available in Ubuntu's sed)
new_file="/export/$(basename "$BATCH_FILE").new"
cp "$BATCH_FILE" "$new_file"
sed -i "s|\($line\)|# \1|" "$new_file"
cp "$new_file" "$BATCH_FILE"
log "DEBUG" "Finished commenting '$line' from '$BATCH_FILE'"
done <<< "$(cat "$BATCH_FILE")"
}
main() {
validate_args
log "INFO" "Processing $BATCH_FILE"
process_batchfile
log "INFO" "Finished processing $BATCH_FILE"
}
# Run main()
main

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Exit 1 if program not run as root
# We need to run 'chown' which requires root`
if [ "$EUID" -ne 0 ]; then
echo "Please run with sudo or as root"
exit 1
fi
# Create base dir
mkdir data
# Create Jellyfin directories
mkdir data/jellyfin
mkdir data/jellyfin/cache
mkdir data/jellyfin/config
mkdir data/jellyfin/media
mkdir data/jellyfin/media/pirate-youtube
# Create batch file
touch data/batch.txt
sudo chown -R 1337:1337 data
Loading…
Cancel
Save