Post

Hyperpipe & Piped

Introduction

Cet article à pour but d’illustrer pas loin de deux mois de lutte acharnés afin de mettre en place une solution qui devait me permettre d’utiliser Youtube; mais sans la publicité.

Prérequis

Il faut avoir lu mes articles sur Traefik (lui est les autres) :)

Fichiers requis

docker-compose.yml

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
version: "3.0"

#
# updated: 2023-06-25
# stack:   hyperpipe
#

x-logging: &x-logging
  logging:
    driver: loki
    options:
      loki-url: "http://loki:3100/loki/api/v1/push"
      loki-retries: "5"
      loki-batch-size: "400"

x-environment: &x-environment
    TZ: "Europe/Paris"
    PUID: 1000
    PGID: 1000

x-common: &x-common
  <<: *x-logging
  restart: "no"
  stop_grace_period: 5s
  stdin_open: true
  tty: true
  privileged: false
  security_opt:
    - no-new-privileges=true
  cap_drop:
    - ALL
  cap_add:
    - KILL
  dns:
    - 1.1.1.1
    - 8.8.8.8
    - 1.0.0.1
    - 8.8.4.4
  ipc: "shareable"
  extra_hosts:
    - "template.home:192.168.0.0"
  environment:
    *x-environment
  user: 1000:1000
  labels:
    com.centurylinklabs.watchtower.enable: true
    logging: "promtail"
    com.stack.name: "common"
    com.stack.service.name: "common"
  devices:
    - /dev/kmsg:/dev/kmsg
  deploy:
    resources:
      limits:
        cpus: "0.50"
        memory: 256M
  ulimits:
    nproc: 65535
    nofile:
      soft: 20000
      hard: 40000
  tmpfs:
    - /tmp:rw,noexec,nosuid,size=64k
  sysctls:
    net.core.somaxconn: 1024
    net.ipv4.tcp_syncookies: 0
x-volume-timezone: &x-volume-timezone "/etc/timezone:/etc/timezone:ro"
x-volume-localtime: &x-volume-localtime "/etc/localtime:/etc/localtime:ro"
x-volume-docker-socket: &x-volume-docker-socket "/var/run/docker.sock:/var/run/docker.sock:rw"
x-volume-cgroups: &x-volume-cgroups "/proc/cgroups:/cgroup:rw"
x-volume-ssl: &x-volume-ssl "/opt/docker/ssl:/ssl:ro"

services:
  piped-db:
    <<: *x-common
    user: 0:0
    cap_add:
      - DAC_OVERRIDE
      - CHOWN
      - FOWNER
      - FSETID
      - SETGID
      - SETUID
      - NET_BIND_SERVICE
      - MKNOD
    container_name: piped-db
    hostname: piped-db
    image: postgres:latest
    restart: unless-stopped
    ports:
      - "5433:5432"
    expose:
      - "5432"
    healthcheck:
      test: ["CMD", "pg_isready", "-q", "-d", "piped", "-U", "root"]
      timeout: 45s
      interval: 10s
      retries: 10
    environment:
      <<: *x-environment
      POSTGRES_DB: piped
      POSTGRES_USER: [username]
      POSTGRES_PASSWORD: [password]
    labels:
      com.stack.name: "piped"
      com.stack.service.name: "db"
    deploy:
      resources:
        limits:
          memory: 512M
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=512M
    volumes:
      - *x-volume-timezone
      - *x-volume-localtime
      - /opt/docker/hyperpipe/datas/db:/var/lib/postgresql/data:rw
  piped-proxy:
    <<: *x-common
    read_only: true
    container_name: piped-proxy
    hostname: piped-proxy
    image: 1337kavin/piped-proxy:latest
    restart: unless-stopped
    environment:
      <<: *x-environment
      UDS: 1
    labels:
      com.stack.name: "piped"
      com.stack.service.name: "proxy"
    deploy:
      resources:
        limits:
          memory: 512M
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=512M
    volumes:
      - *x-volume-timezone
      - *x-volume-localtime
      - /opt/docker/hyperpipe/datas/piped-proxy:/app/socket:rw
  piped-back:
    <<: *x-common
    user: 0:0
    cap_add:
      - DAC_OVERRIDE
      - CHOWN
      - FOWNER
      - FSETID
      - SETGID
      - SETUID
      - NET_BIND_SERVICE
      - MKNOD
    container_name: piped-back
    hostname: piped-back
    #image: 1337kavin/piped:latest
    image: zogg/piped:latest
    restart: unless-stopped
    ports:
      - "8046:8080"
    expose:
      - "8080"
    depends_on:
      - piped-db
    healthcheck:
      test: curl -f http://localhost:8080/ || exit 1
    environment:
      <<: *x-environment
      DSN: ""
    labels:
      com.stack.name: "piped"
      com.stack.service.name: "back"
    deploy:
      resources:
        limits:
          memory: 2G
    tmpfs:
      - /tmp:rw,exec,size=512M
    volumes:
      - *x-volume-timezone
      - *x-volume-localtime
      - /opt/docker/hyperpipe/conf/config.properties:/app/config.properties:ro
  piped-front:
    <<: *x-common
    user: 0:0
    cap_add:
      - DAC_OVERRIDE
      - CHOWN
      - FOWNER
      - FSETID
      - SETGID
      - SETUID
      - NET_BIND_SERVICE
      - MKNOD
    container_name: piped-front
    hostname: piped-front
    image: 1337kavin/piped-frontend:latest
    restart: unless-stopped
    ports:
      - "8047:80"
    expose:
      - "80"
    depends_on:
      - piped-back
    healthcheck:
      test: wget --no-verbose --tries=1 --spider http://localhost:80
    entrypoint: ash -c 'sed -i s/pipedapi.kavin.rocks/pipedapi.domain.com/g /usr/share/nginx/html/assets/* && /docker-entrypoint.sh && nginx -g "daemon off;"'
    labels:
      com.stack.name: "piped"
      com.stack.service.name: "front"
    deploy:
      resources:
        limits:
          memory: 1G
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=512M
    volumes:
      - *x-volume-timezone
      - *x-volume-localtime
  hyperpipe-nginx:
    <<: *x-common
    user: 0:0
    cap_add:
      - DAC_OVERRIDE
      - CHOWN
      - FOWNER
      - FSETID
      - SETGID
      - SETUID
      - NET_BIND_SERVICE
      - MKNOD
    container_name: hyperpipe-nginx
    hostname: hyperpipe-nginx
    image: nginx:latest
    restart: unless-stopped
    depends_on:
      - piped-back
      - piped-front
      - piped-proxy
    ports:
      - "8045:80"
    expose:
      - "80"
    healthcheck:
      test: curl -f http://localhost:80/ || exit 1
    labels:
      com.stack.name: "hyperpipe"
      com.stack.service.name: "nginx"
    deploy:
      resources:
        limits:
          memory: 512M
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=512M
    volumes:
      - *x-volume-timezone
      - *x-volume-localtime
      - /opt/docker/hyperpipe/conf/nginx.conf:/etc/nginx/nginx.conf:ro
      - /opt/docker/hyperpipe/conf/pipedapi.conf:/etc/nginx/conf.d/pipedapi.conf:ro
      - /opt/docker/hyperpipe/conf/pipedproxy.conf:/etc/nginx/conf.d/pipedproxy.conf:ro
      - /opt/docker/hyperpipe/conf/pipedfrontend.conf:/etc/nginx/conf.d/pipedfrontend.conf:ro
      - /opt/docker/hyperpipe/conf/ytproxy.conf:/etc/nginx/snippets/ytproxy.conf:ro
      - /opt/docker/hyperpipe/datas/piped-proxy:/var/run/ytproxy:rw
  hyperpipe-back:
    <<: *x-common
    container_name: hyperpipe-back
    hostname: hyperpipe-back
    image: codeberg.org/hyperpipe/hyperpipe-backend:latest
    restart: unless-stopped
    depends_on:
      - hyperpipe-nginx
    ports:
      - "3771:3000"
    expose:
      - "3000"
    environment:
      <<: *x-environment
      HYP_PROXY: "hyperpipe-proxy.onrender.com"
    labels:
      com.stack.name: "hyperpipe"
      com.stack.service.name: "back"
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=512M
    volumes:
      - *x-volume-timezone
      - *x-volume-localtime
  hyperpipe-front:
    <<: *x-common
    user: 0:0
    cap_add:
      - DAC_OVERRIDE
      - CHOWN
      - FOWNER
      - FSETID
      - SETGID
      - SETUID
      - NET_BIND_SERVICE
      - MKNOD
    container_name: hyperpipe-front
    hostname: hyperpipe-front
    image: codeberg.org/hyperpipe/hyperpipe:latest
    restart: unless-stopped
    depends_on:
      - hyperpipe-back
    ports:
      - "8745:80"
    expose:
      - "80"
    healthcheck:
      test: wget --no-verbose --tries=1 --spider http://localhost
    entrypoint: sh -c 'find /usr/share/nginx/html -type f -exec sed -i s/pipedapi.kavin.rocks/pipedapi.domain.com/g {} \; -exec sed -i s/hyperpipeapi.onrender.com/hyperpipeapi.domain.com/g {} \; && /docker-entrypoint.sh && nginx -g "daemon off;"'
    labels:
      com.stack.name: "hyperpipe"
      com.stack.service.name: "front"
    deploy:
      resources:
        limits:
          memory: 512M
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=512M
    volumes:
      - *x-volume-timezone
      - *x-volume-localtime

config.properties

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# The port to Listen on.
PORT: 8080

# The number of workers to use for the server
HTTP_WORKERS: 8

# Proxy
PROXY_PART: https://pipedproxy.domain.com

# Outgoing HTTP Proxy - eg:  127.0.0.1: 8118
#HTTP_PROXY:  127.0.0.1: 8118

# Captcha Parameters
#CAPTCHA_BASE_URL:  https: //api.capmonster.cloud/
#CAPTCHA_API_KEY:  INSERT_HERE

# Public API URL
API_URL: https://pipedapi.domain.com

# Public Frontend URL
FRONTEND_URL: https://videos.domain.com

# Enable haveibeenpwned compromised password API
COMPROMISED_PASSWORD_CHECK: true

# Disable Registration
DISABLE_REGISTRATION: false

# Feed Retention Time in Days
FEED_RETENTION: 30

# Sentry DSN
SENTRY_DSN:""

# Hibernate properties
hibernate.connection.url: jdbc:postgresql://[adresse ip]:5433/piped
hibernate.connection.driver_class: org.postgresql.Driver
hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate.connection.username: [nom utilisateur]
hibernate.connection.password: [mot de passe]

pipedfrontend.conf

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
server {
    listen 80;
    server_name piped.domain.com;

    set $backend "http://[adresse ip]:8047";

    location / {
        proxy_pass $backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "keep-alive";
    }
}

pipedapi.conf

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
proxy_cache_path /tmp/pipedapi_cache levels=1:2 keys_zone=pipedapi:4m max_size=2g inactive=60m use_temp_path=off;

server {
    listen 80;
    server_name pipedapi.domain.com;

    set $backend "http://[adresse ip]:8046";

    location / {
        proxy_cache pipedapi;
        proxy_pass $backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "keep-alive";
    }
}

pipedproxy.conf

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
    listen 80;
    server_name pipedproxy.domain.com;

    location ~ (/videoplayback|/api/v4/|/api/manifest/) {
        include snippets/ytproxy.conf;
        add_header Cache-Control private always;
    }

    location / {
        include snippets/ytproxy.conf;
        add_header Cache-Control "public, max-age=604800";
    }
}

ytproxy.conf

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
proxy_buffering on;
proxy_buffers 1024 16k;
proxy_set_header X-Forwarded-For "";
proxy_set_header CF-Connecting-IP "";
proxy_hide_header "alt-svc";
sendfile on;
sendfile_max_chunk 512k;
tcp_nopush on;
aio threads=default;
aio_write on;
directio 16m;
proxy_hide_header Cache-Control;
proxy_hide_header etag;
proxy_http_version 1.1;
proxy_set_header Connection keep-alive;
proxy_max_temp_file_size 32m;
access_log off;
proxy_pass http://unix:/var/run/ytproxy/actix.sock;

nginx.conf

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
user root;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;

thread_pool default threads=512 max_queue=65536;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    server_names_hash_bucket_size 128;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log /var/log/nginx/access.log main;
    access_log off;

    sendfile on;
    tcp_nodelay on;

    keepalive_timeout 65;

    resolver [adresse ip] ipv6=off valid=10s;

    server_tokens off;
    tcp_nopush on;
    types_hash_max_size 2048;
    client_body_buffer_size 64K;
    client_header_buffer_size 64k;
    client_max_body_size 128k;
    large_client_header_buffers 8 16k;
    keepalive_requests 100000;
    send_timeout 30;
    client_body_timeout 30;
    client_header_timeout 30;
    reset_timedout_connection on;
    open_file_cache max=2000 inactive=20s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 5;
    open_file_cache_errors off;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options nosniff;
    add_header Strict-Transport-Security "max-age=63072000" always;
    gzip on;
    gzip_static on;
    gzip_min_length 1024;
    gzip_comp_level 6;
    gzip_http_version 1.1;
    gzip_vary on;
    gzip_disable msie6;
    gzip_disable "MSIE [1-6]\.";
    gzip_proxied any;
    gzip_buffers 16 8k;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        text/x-component
        application/x-javascript
        application/xml
        application/javascript
        application/json
        application/xml+rss
        application/rss+xml
        application/atom+xml
        font/truetype
        font/opentype
        application/vnd.ms-fontobject
        image/svg+xml
        application/geo+json
        application/ld+json
        application/manifest+json
        application/rdf+xml
        application/wasm
        application/x-web-app-manifest+json
        application/xhtml+xml
        font/eot
        font/otf
        font/ttf
        image/bmp
        text/cache-manifest
        text/calendar
        text/markdown
        text/vcard
        text/vnd.rim.location.xloc
        text/vtt
        text/x-cross-domain-policy
        application/x-font-ttf
        image/x-icon;

    include /etc/nginx/conf.d/*.conf;
}

platform.sh

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

# Used in Docker build to set platform dependent variables

case $TARGETARCH in

    "amd64")
	echo "x86_64-unknown-linux-gnu" > /.platform
	echo "" > /.compiler
	;;
    "arm64")
	echo "aarch64-unknown-linux-gnu" > /.platform
	echo "gcc-aarch64-linux-gnu" > /.compiler
	;;
    "arm")
	echo "armv7-unknown-linux-gnueabihf" > /.platform
	echo "gcc-arm-linux-gnueabihf" > /.compiler
	;;
esac

build.sh

[Fichier]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
# 2023-06-25

clear
cd "$(dirname "$0")" || exit 1

IMAGE_BASE=zogg/piped
IMAGE_NAME_LATEST=${IMAGE_BASE}:latest

export DOCKER_CLI_EXPERIMENTAL=enabled
docker run --privileged --rm tonistiigi/binfmt --install all

export DOCKER_DEFAULT_PLATFORM=linux/amd64
docker buildx build --pull \
    --platform=linux/amd64 \
    --output=type=docker \
    --build-arg TZ=Europe/Paris \
    --build-arg CONCURRENCY=$(nproc) \
    -t "${IMAGE_NAME_LATEST}" \
    . 2>&1 | tee build.log

exit 0

Préparation

Il est nécessaire de construire une image modifée de Piped pour que l’ensemble puisse fonctionner.

Sans cette modification, le conteneur piped-back refusera se lancer correctement avec l’erreur suivante :

Exception in thread “main” java.lang.IllegalArgumentException: DSN is required. Use empty string to disable SDK.

Pour ce faire, dans un shell, vous tappez :

1
git clone https://github.com/TeamPiped/Piped-Backend

Dans ce répertoire, vous copier les fichiers suivants :

  • plateform.sh
  • build.sh

Ensuite, vous vous rendez dans le répertoire src/main/java/me/kavin/piped.

Vous éditez le fichier Main.java et vous commentez le code Sentry.init somme suit :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {

    public static void main(String[] args) throws Exception {

        NewPipe.init(new DownloaderImpl(), new Localization("en", "US"), ContentCountry.DEFAULT, Multithreading.getCachedExecutor());
        YoutubeStreamExtractor.forceFetchAndroidClient(true);
        YoutubeStreamExtractor.forceFetchIosClient(true);

        /*
        Sentry.init(options -> {
            options.setDsn(Constants.SENTRY_DSN);
            options.setRelease(Constants.VERSION);
            options.addIgnoredExceptionForType(ErrorResponse.class);
            options.setTracesSampleRate(0.1);
        });
        */

        Injector.useSpecializer();

Puis vous revenez à la racine du projet; là ou vous avez cloner le dépôt Git.

Et vous lancez la compilation de l’image Docker :

1
sudo bash build.sh

Et c’est cette image qui sera utilisée dans cette partie de la stack :

1
2
3
4
  piped-back:
    ...
    #image: 1337kavin/piped:latest
    image: zogg/piped:latest

Mise en place

Je partirai du principe que vous utilisez Portainer pour gérer vos stack Docker.

Il vous faudra créer les répertoires suivants (ou adapter au besoin) :

  • /opt/docker/hyperpipe/conf/
  • /opt/docker/hyperpipe/datas/db/
  • /opt/docker/hyperpipe/datas/piped-proxy/

Précisions

L’utilisation de ce genre d’outil requiert une bonne bande passante (à la fois montante et descendante).

Conclusion

Avec cette solution vous disposer d’un moyen efficace de visionner des vidéos Youtube ou d’écouter de la musique sur Youtube Music; et le tout sans publicité !

Cet article est sous licence CC BY 4.0 par l'auteur.

© 2022- Olivier. Certains droits réservés.

Propulsé par τζ avec le thème Χ