├── .gitattributes
├── .github
└── workflows
│ ├── dockerhub-readme-push.yml
│ ├── image-build-Main.yml
│ └── image-build-development.yml
├── .gitignore
├── Dockerfile
├── README.md
├── copy-files
├── apache2
│ ├── conf-available
│ │ └── serve-cgi-bin.conf
│ ├── mods-available
│ │ ├── deflate.conf
│ │ └── mime.conf
│ └── sites-available
│ │ └── 000-default.conf
├── cgi-bin
│ ├── .tidal-dl.json
│ ├── main.py
│ ├── py38.cgi
│ └── tidaldl.py
├── lib
│ └── hoge.py
├── requierments.txt
├── tidal-login.sh
└── webpage
│ └── index.html
├── docker-compose.yml
├── logos
└── tidal-icon.png
├── notes.txt
└── public_html
└── test.html
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.yml merge=ours
2 |
3 |
--------------------------------------------------------------------------------
/.github/workflows/dockerhub-readme-push.yml:
--------------------------------------------------------------------------------
1 | name: Push Readme to Docker Hub
2 |
3 | # Only run on README change in main
4 | on:
5 | push:
6 | branches: [ master ]
7 | paths:
8 | - README.md
9 |
10 | jobs:
11 | dockerHubPush:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: checkout code
15 | uses: actions/checkout@v3
16 |
17 | - name: Update README
18 | uses: peter-evans/dockerhub-description@v3
19 | with:
20 | username: ${{ secrets.DOCKER_USERNAME }}
21 | password: ${{ secrets.DOCKER_ACTUAL_PASSWORD }}
22 | repository: rgnet1/tidal-dl
23 | short-description: ${{ github.event.repository.description }}
24 |
--------------------------------------------------------------------------------
/.github/workflows/image-build-Main.yml:
--------------------------------------------------------------------------------
1 | name: Build and Push - Production
2 |
3 | on:
4 | workflow_dispatch:
5 | branches:
6 | - master
7 | push:
8 | branches: master
9 | paths-ignore:
10 | - '**/README.md'
11 | - '**.yml'
12 |
13 | jobs:
14 | Docker-Build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: checkout code
18 | uses: actions/checkout@v3
19 |
20 | - name: Set up QEMU
21 | id: buildx
22 | uses: docker/setup-qemu-action@v2
23 | with:
24 | version: latest
25 |
26 | - name: Set up Docker Buildx
27 | uses: docker/setup-buildx-action@v2
28 |
29 | - name: Login to DockerHub
30 | uses: docker/login-action@v2
31 | with:
32 | username: ${{ secrets.DOCKER_USERNAME }}
33 | password: ${{ secrets.DOCKER_PASSWORD }}
34 |
35 | - name: Build and push to DockerHub
36 | uses: docker/build-push-action@v3
37 | with:
38 | context: .
39 | platforms: linux/amd64,linux/arm/v7,linux/arm64
40 | push: true
41 | tags: rgnet1/tidal-dl:latest
42 |
--------------------------------------------------------------------------------
/.github/workflows/image-build-development.yml:
--------------------------------------------------------------------------------
1 | name: Build and Push - Development
2 |
3 | on:
4 | workflow_dispatch:
5 | branches:
6 | - development
7 |
8 | push:
9 | branches:
10 | - development
11 | paths-ignore:
12 | - '**/README.md'
13 | - '**.yml'
14 | - 'notes.txt'
15 |
16 | jobs:
17 | Docker-Build-Developmnet:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: checkout code
21 | uses: actions/checkout@v3
22 |
23 | - name: Set up QEMU
24 | id: buildx
25 | uses: docker/setup-qemu-action@v2
26 | with:
27 | version: latest
28 |
29 | - name: Set up Docker Buildx
30 | uses: docker/setup-buildx-action@v2
31 |
32 | - name: Login to DockerHub
33 | uses: docker/login-action@v2
34 | with:
35 | username: ${{ secrets.DOCKER_USERNAME }}
36 | password: ${{ secrets.DOCKER_PASSWORD }}
37 |
38 | - name: Build and push to DockerHub
39 | uses: docker/build-push-action@v3
40 | with:
41 | context: .
42 | platforms: linux/amd64,linux/arm/v7,linux/arm64
43 | push: true
44 | tags: rgnet1/tidal-dl:development
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .tidal-dl.*
2 | links.txt
3 | .vscode/**
4 | *.pyc
5 | __pycache__/*
6 | Untitled-1
7 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Docker file for python simple tidal-dl web server build
2 |
3 | FROM ubuntu:20.04
4 |
5 | ENV DEBIAN_FRONTEND=noninteractive \
6 | APACHE_RUN_USER=www-data \
7 | APACHE_RUN_GROUP=www-data \
8 | APACHE_LOG_DIR=/var/log/apache2 \
9 | APACHE_PID_FILE=/var/run/apache2.pid \
10 | APACHE_RUN_DIR=/var/run/apache2 \
11 | APACHE_LOCK_DIR=/var/lock/apache2
12 |
13 | # Updates and installs
14 | RUN apt update && apt -y install \
15 | software-properties-common \
16 | apache2 \
17 | nano \
18 | python3 \
19 | python3-pip
20 |
21 | # Copy necessary files into container
22 | COPY copy-files/ ./copy-files/
23 |
24 | # set up container enviornment:
25 | RUN pip3 install -r copy-files/requierments.txt &&\
26 | # cp copy-files/settings.py /usr/local/lib/python3.8/dist-packages/tidal_dl/settings.py && \
27 | mkdir -p $APACHE_RUN_DIR $APACHE_LOCK_DIR $APACHE_LOG_DIR && \
28 | mkdir -p /production/www/cgi-bin/download/Album && \
29 | mkdir -p /production/www/lib && \
30 | cp -r copy-files/cgi-bin/* /production/www/cgi-bin/ && \
31 | cp -r copy-files/lib/* /production/www/lib/ && \
32 | cp -r copy-files/apache2/* /etc/apache2/ && \
33 | cp -r copy-files/webpage/* /var/www/html/ && \
34 | ln -s /etc/apache2/mods-available/cgi.load /etc/apache2/mods-enabled/cgi.load && \
35 | chgrp www-data /production/www/cgi-bin/ && \
36 | chmod g+rwx /production/www/cgi-bin/ && \
37 | chown -R www-data: /production/www/cgi-bin/download/ && \
38 | chmod 755 production/www/cgi-bin/download/ && \
39 | chgrp www-data /var/www/ && \
40 | chmod g+rwxs /var/www/ && \
41 | cp copy-files/tidal-login.sh . && \
42 | chmod +x tidal-login.sh
43 |
44 | EXPOSE 80
45 | ENTRYPOINT [ "/usr/sbin/apache2" ]
46 | CMD ["-D", "FOREGROUND"]
47 |
48 | # Login as www-data user: su -l www-data -s /bin/bash
49 | # edit settings file: nano /usr/local/lib/python3.8/dist-packages/tidal_dl/settings.py
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rgnet1/tidal-dl
2 | [](https://img.shields.io/github/workflow/status/rgnet1/tidal-dl/Build)
3 | 
4 |
5 | This is a simple web server that allows you to run yaronzz/Tidal-Media-Downloader
6 | from a web browser. You must have an active tidal subscription.
7 |
8 | ## Usage
9 | Here are some example snippets to help you get started creating a container.
10 | ### docker-compose ([recommended](https://docs.linuxserver.io/general/docker-compose))
11 |
12 | Compatible with docker-compose v3 schemas.
13 |
14 | ```yaml
15 | version: "3"
16 |
17 | services:
18 | tidal-dl:
19 | container_name: tidal-dl
20 | image: rgnet1/tidal-dl:latest
21 | ports:
22 | - "8885:80"
23 | volumes:
24 | - '~/download/:/production/www/cgi-bin/download/'
25 | - '~/configuration/:/production/www/cgi-bin/configuration/'
26 |
27 | ```
28 |
29 | ### docker cli
30 |
31 | ```
32 | docker run -d \
33 | --name=tidal-dl \
34 | -p 8885:80 \
35 | -v ~/download/:/production/www/cgi-bin/download/ \
36 | -v ~/configuration/:/production/www/cgi-bin/configuration/ \
37 | rgnet1/tidal-dl
38 |
39 | ```
40 | **_Note:_** If you run into issues running the container, please try using
41 | privliged mode by adding the flag ```--privileged``` to docker cli or
42 | ```privileged: true``` to docker-compose. Debian based hosts will need privleged mode.
43 |
44 | ## Application Setup
45 | ### Set up with existing tidal-dl configuration
46 | Only the downloads directory volume map is required. If you want to keep your tidal configuration
47 | persistant so you don't have to log in every time you star the container, you must map
48 | the configuration folder. If you are provideding your own configuration you must place
49 | the two files (.tidal-dl.json and .tidal-dl.token.json) in the configuration folder
50 |
51 | | Your host location | Container location (don't change) | Notes |
52 | | :----: | --- | --- |
53 | | ~/download/ | /production/www/cgi-bin/download/ | Files will download here
54 | | ~/configuration/ | /production/www/cgi-bin/configuration/ | .tidal-dl.json and .tidal-dl.token.json will be placed here
55 |
56 |
57 | **_Note:_** Just volume mount the configuration folder, and make sure your config file and token file are in it. No need to volume mount the files seperatly.
58 |
59 | ### Set up via container login
60 | If you wish to start fresh and log into tidal you can. Simply insert a link and try to download it.
61 |
62 | If you are not logged
63 | into tidal, your login link will be generated for you as you try to download a song. You will need to copy and paste that link into a web browser to
64 | login.
65 |
66 | Feel free to open an issue if you have issues logging in.
67 |
68 | You can Access from the below URL after run docker container:
69 |
70 | * [http://localhost:8885](http://localhost:8885)
71 |
72 | ## Parameters
73 |
74 | Container images are configured using parameters passed at runtime (such as those above). These parameters are separated by a colon and indicate ` link:", form["textcontent"].value)
33 | f = open("/production/www/cgi-bin/links.txt", "w+")
34 | f.write(form["textcontent"].value)
35 | f.close()
36 | import tidaldl.py
--------------------------------------------------------------------------------
/copy-files/cgi-bin/py38.cgi:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3.8
2 |
3 | import hoge
4 |
5 | print("Content-type: text/html")
6 | print("\n")
7 | print("")
8 | print(hoge.hoge())
9 | print('This cgi script written by python.')
10 | print("")
11 |
--------------------------------------------------------------------------------
/copy-files/cgi-bin/tidaldl.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3 -u
2 | # @author: Ramzey Ghanaim
3 | #
4 | # tidaldl.py
5 | # This program is desinged to automate and downlad all tidal links
6 | # users provide into a text file at one time.
7 | #
8 | # Instructions:
9 | # Copy the links to the songs you want, past them into the
10 | # text document, and run the code
11 | #
12 | # Dependencies:
13 | # pexpect, python3
14 | import pexpect
15 | import sys
16 | import re
17 | import time
18 | import shutil, os
19 | from pexpect.expect import searcher_re
20 | import filecmp
21 |
22 | ansi_escape = re.compile(rb'\x1B[@-_][0-?]*[ -/]*[@-~]')
23 | FILENAME = "/production/www/cgi-bin/links.txt"
24 | # FILENAME = "../links.txt"
25 | totalSongCount = 0
26 | print('''
27 |
30 | ''')
31 | print(" ")
32 | # waitAgain()
33 | #
34 | # This function waits to expect the "enter choice" output from tidaldl
35 | # this output means that the downald is completed or failed, and we are
36 | # waiting for the next song
37 | #
38 | # @param: type - string - type of link we are downloading: album, track
39 | def waitAgain(type):
40 | global totalSongCount
41 | # y = 0 y=1
42 | y = tidal.expect(['.*Enter Choice:.*', '.*SUCCESS.*'],timeout=50)
43 |
44 | # Current link is completed. tidal-dl is waiting for next link
45 | if y == 0:
46 | print("Finished download from current", type, "
\n")
47 | # sys.stdout.write(ansi_escape.sub(b'',tidal.before).decode("utf-8"))
48 | # sys.stdout.write(ansi_escape.sub(b'',tidal.after).decode("utf-8"))
49 | return 0
50 |
51 | # successfully download one song, but the next song is downloading
52 | if y == 1:
53 | # remove ansi color, and decode byte stream into utf-8 for python
54 | # compatability
55 | successString = ansi_escape.sub(b'',tidal.after).decode("utf-8")
56 |
57 | # Split string in half at [success], because file name is to the right (index 1)
58 | # EX: ".......... [SUCCESS] .....
\n")
78 | sys.stdout.flush()
79 | totalSongCount +=1
80 | return -1
81 |
82 | def set_up_config_folder(startup=False):
83 |
84 | settings_path = '/production/www/cgi-bin/.tidal-dl.json'
85 | token_path = '/production/www/cgi-bin/.tidal-dl.token.json'
86 | settings_dest = '/production/www/cgi-bin/configuration/settings.json'
87 | token_dest = '/production/www/cgi-bin/configuration/token.json'
88 |
89 | # Move user provided file to tidal-dl
90 | if (os.path.exists(token_dest)) and ( (not os.path.exists(token_path)) or (not filecmp.cmp(token_dest, token_path))):
91 | shutil.copy2(token_dest, token_path)
92 | print("Copying provided token
\n")
93 | os.chmod(token_path, 0o666)
94 | if (os.path.exists(settings_dest)) and ( (not os.path.exists(settings_path)) or (not filecmp.cmp(settings_dest, settings_path))):
95 | shutil.copy2(settings_dest, settings_path)
96 | print("Copying settings
\n")
97 | os.chmod(settings_path, 0o666)
98 | return
99 |
100 | # Move generated file to configuration/
101 | if (os.path.exists(token_path)) and ( (not os.path.exists(token_dest)) or (not filecmp.cmp(token_dest, token_path))):
102 | shutil.copy2(token_path, token_dest)
103 | print("Copying generated token file to configuration folder
\n")
104 | os.chmod(token_dest, 0o666)
105 | if (os.path.exists(settings_path)) and ( (not os.path.exists(settings_dest)) or (not filecmp.cmp(settings_dest, settings_path))):
106 | shutil.copy2(settings_path, settings_dest)
107 | print("Copying settings file to configuration folder
\n")
108 | os.chmod(settings_dest, 0o666)
109 |
110 |
111 | def login(tidal):
112 |
113 | print("\nWaiting for you to register....
\n")
114 | string_output = ansi_escape.sub(b'',tidal.after).decode("utf-8")
115 | login_url = re.search("(?P
\n")
117 | print(login_url + "
\n")
118 | wait_time = string_output.split(" minutes")[0].split()[-1:][0]
119 | if wait_time.isnumeric():
120 | wait_time = int(wait_time)
121 | else:
122 | wait_time = 5
123 | print(f"You have {wait_time} minutes
\n")
124 | x = tidal.expect(['.*Enter Choice:.*', '.*Waiting for authorization.*', '.*APIKEY index:.*'],timeout=(wait_time * 60))
125 | if x == 0:
126 | print("Login Complete. Please try your link(s) again
\n")
127 | set_up_config_folder()
128 | else:
129 | print("Login did not work
\n")
130 | print(ansi_escape.sub(b'',tidal.before).decode("utf-8"))
131 | print(ansi_escape.sub(b'',tidal.after).decode("utf-8"))
132 |
133 |
134 | set_up_config_folder()
135 |
136 | # read file
137 | queue = open(FILENAME, 'r')
138 |
139 |
140 | # Start up the tidal-dl
141 | tidal = pexpect.spawn("tidal-dl")
142 | x = 1
143 | first = True
144 | for line in queue:
145 | line = line.strip()
146 | if len(line) <= 4:
147 | continue
148 | type = "unknown"
149 | if "album" in line:
150 | type = "album"
151 | elif "track" in line:
152 | type = "track"
153 | elif "playlist" in line:
154 | type = "playlist"
155 | elif "video" in line:
156 | type = "video"
157 | try:
158 | print("----------------Starting new download. New", type,"----------------
\n")
159 | # Only check Enter choice first time, because waitAgain() function
160 | # checks it after the first time
161 | if first:
162 | x = tidal.expect(['.*Enter Choice:.*', '.*Waiting for authorization.*', '.*APIKEY index:.*'],timeout=10)
163 | first = False
164 | else:
165 | x = 0
166 | if x == 0:
167 | y = -1
168 | tidal.send(line.strip() + "\n")
169 | while y == -1:
170 | y = waitAgain(type)
171 | # print("Done with current link")
172 | if x == 1:
173 | # sys.stdout.write(ansi_escape.sub(b'',tidal.before).decode("utf-8"))
174 | # sys.stdout.write(ansi_escape.sub(b'',tidal.after).decode("utf-8"))
175 | login(tidal)
176 | tidal.kill(0)
177 | print("
\n")
180 | # print("unRAID users: simply open the console and run: ./tidal-login.sh
\n")
181 | # sttart interactie mode
182 | # tidal.interact()
183 | if x == 2:
184 | # Select API key option 4 - Valid = true, Formats - all
185 | # May need to double check this prior to upgrading tidal-dl version
186 | tidal.send('4\n')
187 | x = tidal.expect(['.*Enter Choice:.*', '.*Waiting for authorization.*', '.*APIKEY index:.*'],timeout=10)
188 | if x == 1:
189 | login(tidal)
190 | else:
191 | print("ERROR. API Key didn't work
\n")
192 | print(ansi_escape.sub(b'',tidal.before).decode("utf-8"))
193 | print(ansi_escape.sub(b'',tidal.after).decode("utf-8"))
194 |
195 | tidal.kill(0)
196 | print("
Insert your links, one link per line on the left
33 |