├── .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 | [![GitHub Workflow Status](https://github.com/docker/buildx/workflows/build/badge.svg)](https://img.shields.io/github/workflow/status/rgnet1/tidal-dl/Build) 3 | ![Docker Pulls](https://img.shields.io/docker/pulls/rgnet1/tidal-dl) 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 `:` respectively. For example, `-p 8885:80` would expose port `80` from inside the container to be accessible from the host's IP on port `8885` outside the container. 75 | 76 | 77 | | Parameter | Function | 78 | | :----: | --- | 79 | | `-p 80` | Tidal-dl Web UI (required)| 80 | | `-v /production/www/cgi-bin/download/` | Contains the download directory for tidal-dl (required)| 81 | | `-v /production/www/cgi-bin/configuration/` | Contains tidal-dl settings (optional but recommended) | 82 | 83 | 84 | 85 | ### Tidal-dl settings 86 | We use default tidal-dl settings if you do not volume mount your own settings. If you wish to 87 | change the tidal-dl settings, you must map the configuration folder and run the container. 88 | Then you can modify the tidal-dl settings file. 89 | 90 | **_Note:_** Never change the `downloadPath` variable inside the contianer's tidal-dl.json settings. The current path has specfic linux permissions that allows web users to write to. 91 | Use volume mapping to map this required directory to your 92 | directory of choice. 93 | 94 | 95 | ## Supported Architectures 96 | The architectures supported by this image are: 97 | 98 | | Architecture | Tag | 99 | | :----: | --- | 100 | | amd64 | latest | 101 | | arm64 | latest | 102 | | arm | latest | 103 | 104 | ## Docker Hub Link 105 | You can find this project on Docker Hub [here](https://hub.docker.com/repository/docker/rgnet1/tidal-dl) 106 | 107 | ### References 108 | * [Usage of docker with apache2 and cgi](https://github.com/pyohei/docker-cgi-python) 109 | * [Tidal-dl](https://github.com/yaronzz/Tidal-Media-Downloader) 110 | -------------------------------------------------------------------------------- /copy-files/apache2/conf-available/serve-cgi-bin.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | Define ENABLE_USR_LIB_CGI_BIN 4 | 5 | 6 | 7 | Define ENABLE_USR_LIB_CGI_BIN 8 | 9 | 10 | 11 | ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ 12 | 13 | AllowOverride None 14 | Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch 15 | Require all granted 16 | 17 | SetEnv PYTHONPATH /production/www/lib 18 | ScriptAlias /cgi-bin2/ /production/www/cgi-bin/ 19 | 20 | AllowOverride None 21 | Require all granted 22 | #Options +ExecCGI 23 | Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch 24 | AddHandler cgi-script .cgi .py 25 | #Options None 26 | Order allow,deny 27 | Allow from all 28 | 29 | 30 | 31 | 32 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 33 | -------------------------------------------------------------------------------- /copy-files/apache2/mods-available/deflate.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | #AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css 4 | AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript 5 | AddOutputFilterByType DEFLATE application/rss+xml 6 | AddOutputFilterByType DEFLATE application/xml 7 | 8 | 9 | 10 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet -------------------------------------------------------------------------------- /copy-files/apache2/mods-available/mime.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 4 | # TypesConfig points to the file containing the list of mappings from 5 | # filename extension to MIME-type. 6 | # 7 | TypesConfig /etc/mime.types 8 | 9 | # 10 | # AddType allows you to add to or override the MIME configuration 11 | # file mime.types for specific file types. 12 | # 13 | #AddType application/x-gzip .tgz 14 | # 15 | # AddEncoding allows you to have certain browsers uncompress 16 | # information on the fly. Note: Not all browsers support this. 17 | # Despite the name similarity, the following Add* directives have 18 | # nothing to do with the FancyIndexing customization directives above. 19 | # 20 | #AddEncoding x-compress .Z 21 | #AddEncoding x-gzip .gz .tgz 22 | #AddEncoding x-bzip2 .bz2 23 | # 24 | # If the AddEncoding directives above are commented-out, then you 25 | # probably should define those extensions to indicate media types: 26 | # 27 | AddType application/x-compress .Z 28 | AddType application/x-gzip .gz .tgz 29 | AddType application/x-bzip2 .bz2 30 | 31 | # 32 | # DefaultLanguage and AddLanguage allows you to specify the language of 33 | # a document. You can then use content negotiation to give a browser a 34 | # file in a language the user can understand. 35 | # 36 | # Specify a default language. This means that all data 37 | # going out without a specific language tag (see below) will 38 | # be marked with this one. You probably do NOT want to set 39 | # this unless you are sure it is correct for all cases. 40 | # 41 | # * It is generally better to not mark a page as 42 | # * being a certain language than marking it with the wrong 43 | # * language! 44 | # 45 | # DefaultLanguage nl 46 | # 47 | # Note 1: The suffix does not have to be the same as the language 48 | # keyword --- those with documents in Polish (whose net-standard 49 | # language code is pl) may wish to use "AddLanguage pl .po" to 50 | # avoid the ambiguity with the common suffix for perl scripts. 51 | # 52 | # Note 2: The example entries below illustrate that in some cases 53 | # the two character 'Language' abbreviation is not identical to 54 | # the two character 'Country' code for its country, 55 | # E.g. 'Danmark/dk' versus 'Danish/da'. 56 | # 57 | # Note 3: In the case of 'ltz' we violate the RFC by using a three char 58 | # specifier. There is 'work in progress' to fix this and get 59 | # the reference data for rfc1766 cleaned up. 60 | # 61 | # Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) 62 | # English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de) 63 | # Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja) 64 | # Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn) 65 | # Norwegian (no) - Polish (pl) - Portugese (pt) 66 | # Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv) 67 | # Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW) 68 | # 69 | AddLanguage am .amh 70 | AddLanguage ar .ara 71 | AddLanguage be .be 72 | AddLanguage bg .bg 73 | AddLanguage bn .bn 74 | AddLanguage br .br 75 | AddLanguage bs .bs 76 | AddLanguage ca .ca 77 | AddLanguage cs .cz .cs 78 | AddLanguage cy .cy 79 | AddLanguage da .dk 80 | AddLanguage de .de 81 | AddLanguage dz .dz 82 | AddLanguage el .el 83 | AddLanguage en .en 84 | AddLanguage eo .eo 85 | # es is ecmascript in /etc/mime.types 86 | RemoveType es 87 | AddLanguage es .es 88 | AddLanguage et .et 89 | AddLanguage eu .eu 90 | AddLanguage fa .fa 91 | AddLanguage fi .fi 92 | AddLanguage fr .fr 93 | AddLanguage ga .ga 94 | AddLanguage gl .glg 95 | AddLanguage gu .gu 96 | AddLanguage he .he 97 | AddLanguage hi .hi 98 | AddLanguage hr .hr 99 | AddLanguage hu .hu 100 | AddLanguage hy .hy 101 | AddLanguage id .id 102 | AddLanguage is .is 103 | AddLanguage it .it 104 | AddLanguage ja .ja 105 | AddLanguage ka .ka 106 | AddLanguage kk .kk 107 | AddLanguage km .km 108 | AddLanguage kn .kn 109 | AddLanguage ko .ko 110 | AddLanguage ku .ku 111 | AddLanguage lo .lo 112 | AddLanguage lt .lt 113 | AddLanguage ltz .ltz 114 | AddLanguage lv .lv 115 | AddLanguage mg .mg 116 | AddLanguage mk .mk 117 | AddLanguage ml .ml 118 | AddLanguage mr .mr 119 | AddLanguage ms .msa 120 | AddLanguage nb .nob 121 | AddLanguage ne .ne 122 | AddLanguage nl .nl 123 | AddLanguage nn .nn 124 | AddLanguage no .no 125 | AddLanguage pa .pa 126 | AddLanguage pl .po 127 | AddLanguage pt-BR .pt-br 128 | AddLanguage pt .pt 129 | AddLanguage ro .ro 130 | AddLanguage ru .ru 131 | AddLanguage sa .sa 132 | AddLanguage se .se 133 | AddLanguage si .si 134 | AddLanguage sk .sk 135 | AddLanguage sl .sl 136 | AddLanguage sq .sq 137 | AddLanguage sr .sr 138 | AddLanguage sv .sv 139 | AddLanguage ta .ta 140 | AddLanguage te .te 141 | AddLanguage th .th 142 | AddLanguage tl .tl 143 | RemoveType tr 144 | # tr is troff in /etc/mime.types 145 | AddLanguage tr .tr 146 | AddLanguage uk .uk 147 | AddLanguage ur .ur 148 | AddLanguage vi .vi 149 | AddLanguage wo .wo 150 | AddLanguage xh .xh 151 | AddLanguage zh-CN .zh-cn 152 | AddLanguage zh-TW .zh-tw 153 | 154 | # 155 | # Commonly used filename extensions to character sets. You probably 156 | # want to avoid clashes with the language extensions, unless you 157 | # are good at carefully testing your setup after each change. 158 | # See http://www.iana.org/assignments/character-sets for the 159 | # official list of charset names and their respective RFCs. 160 | # 161 | AddCharset us-ascii .ascii .us-ascii 162 | AddCharset ISO-8859-1 .iso8859-1 .latin1 163 | AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen 164 | AddCharset ISO-8859-3 .iso8859-3 .latin3 165 | AddCharset ISO-8859-4 .iso8859-4 .latin4 166 | AddCharset ISO-8859-5 .iso8859-5 .cyr .iso-ru 167 | AddCharset ISO-8859-6 .iso8859-6 .arb .arabic 168 | AddCharset ISO-8859-7 .iso8859-7 .grk .greek 169 | AddCharset ISO-8859-8 .iso8859-8 .heb .hebrew 170 | AddCharset ISO-8859-9 .iso8859-9 .latin5 .trk 171 | AddCharset ISO-8859-10 .iso8859-10 .latin6 172 | AddCharset ISO-8859-13 .iso8859-13 173 | AddCharset ISO-8859-14 .iso8859-14 .latin8 174 | AddCharset ISO-8859-15 .iso8859-15 .latin9 175 | AddCharset ISO-8859-16 .iso8859-16 .latin10 176 | AddCharset ISO-2022-JP .iso2022-jp .jis 177 | AddCharset ISO-2022-KR .iso2022-kr .kis 178 | AddCharset ISO-2022-CN .iso2022-cn .cis 179 | AddCharset Big5 .Big5 .big5 .b5 180 | AddCharset cn-Big5 .cn-big5 181 | # For russian, more than one charset is used (depends on client, mostly): 182 | AddCharset WINDOWS-1251 .cp-1251 .win-1251 183 | AddCharset CP866 .cp866 184 | AddCharset KOI8 .koi8 185 | AddCharset KOI8-E .koi8-e 186 | AddCharset KOI8-r .koi8-r .koi8-ru 187 | AddCharset KOI8-U .koi8-u 188 | AddCharset KOI8-ru .koi8-uk .ua 189 | AddCharset ISO-10646-UCS-2 .ucs2 190 | AddCharset ISO-10646-UCS-4 .ucs4 191 | AddCharset UTF-7 .utf7 192 | AddCharset UTF-8 .utf8 193 | AddCharset UTF-16 .utf16 194 | AddCharset UTF-16BE .utf16be 195 | AddCharset UTF-16LE .utf16le 196 | AddCharset UTF-32 .utf32 197 | AddCharset UTF-32BE .utf32be 198 | AddCharset UTF-32LE .utf32le 199 | AddCharset euc-cn .euc-cn 200 | AddCharset euc-gb .euc-gb 201 | AddCharset euc-jp .euc-jp 202 | AddCharset euc-kr .euc-kr 203 | #Not sure how euc-tw got in - IANA doesn't list it??? 204 | AddCharset EUC-TW .euc-tw 205 | AddCharset gb2312 .gb2312 .gb 206 | AddCharset iso-10646-ucs-2 .ucs-2 .iso-10646-ucs-2 207 | AddCharset iso-10646-ucs-4 .ucs-4 .iso-10646-ucs-4 208 | AddCharset shift_jis .shift_jis .sjis 209 | AddCharset BRF .brf 210 | 211 | # 212 | # AddHandler allows you to map certain file extensions to "handlers": 213 | # actions unrelated to filetype. These can be either built into the server 214 | # or added with the Action directive (see below) 215 | # 216 | # To use CGI scripts outside of ScriptAliased directories: 217 | # (You will also need to add "ExecCGI" to the "Options" directive.) 218 | # 219 | AddHandler cgi-script .cgi 220 | 221 | # 222 | # For files that include their own HTTP headers: 223 | # 224 | #AddHandler send-as-is asis 225 | 226 | # 227 | # For server-parsed imagemap files: 228 | # 229 | #AddHandler imap-file map 230 | 231 | # 232 | # For type maps (negotiated resources): 233 | # (This is enabled by default to allow the Apache "It Worked" page 234 | # to be distributed in multiple languages.) 235 | # 236 | AddHandler type-map var 237 | 238 | # 239 | # Filters allow you to process content before it is sent to the client. 240 | # 241 | # To parse .shtml files for server-side includes (SSI): 242 | # (You will also need to add "Includes" to the "Options" directive.) 243 | # 244 | AddType text/html .shtml 245 | AddOutputFilter INCLUDES .shtml 246 | 247 | 248 | 249 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 250 | -------------------------------------------------------------------------------- /copy-files/apache2/sites-available/000-default.conf: -------------------------------------------------------------------------------- 1 | 2 | # The ServerName directive sets the request scheme, hostname and port that 3 | # the server uses to identify itself. This is used when creating 4 | # redirection URLs. In the context of virtual hosts, the ServerName 5 | # specifies what hostname must appear in the request's Host: header to 6 | # match this virtual host. For the default virtual host (this file) this 7 | # value is not decisive as it is used as a last resort host regardless. 8 | # However, you must set it for any further virtual host explicitly. 9 | #ServerName www.example.com 10 | 11 | ServerAdmin webmaster@localhost 12 | DocumentRoot /var/www/html 13 | 14 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, 15 | # error, crit, alert, emerg. 16 | # It is also possible to configure the loglevel for particular 17 | # modules, e.g. 18 | #LogLevel info ssl:warn 19 | 20 | ErrorLog ${APACHE_LOG_DIR}/error.log 21 | CustomLog ${APACHE_LOG_DIR}/access.log combined 22 | 23 | # For most configuration files from conf-available/, which are 24 | # enabled or disabled at a global level, it is possible to 25 | # include a line for only one particular virtual host. For example the 26 | # following line enables the CGI configuration for this host only 27 | # after it has been globally disabled with "a2disconf". 28 | Include conf-available/serve-cgi-bin.conf 29 | 30 | 31 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 32 | -------------------------------------------------------------------------------- /copy-files/cgi-bin/.tidal-dl.json: -------------------------------------------------------------------------------- 1 | { 2 | "albumFolderFormat": "{ArtistName}/{AlbumTitle} ({AlbumYear})", 3 | "apiKeyIndex": 4, 4 | "audioQuality": "Master", 5 | "checkExist": false, 6 | "downloadPath": "/production/www/cgi-bin/download/", 7 | "includeEP": false, 8 | "language": "0", 9 | "lyricFile": true, 10 | "multiThread": null, 11 | "saveAlbumInfo": false, 12 | "saveCovers": true, 13 | "showProgress": true, 14 | "showTrackInfo": true, 15 | "trackFileFormat": "{TrackNumber}-{TrackTitle}", 16 | "usePlaylistFolder": false, 17 | "videoFileFormat": "{VideoNumber} - {ArtistName} - {VideoTitle}{ExplicitFlag}", 18 | "videoQuality": "P1080" 19 | } -------------------------------------------------------------------------------- /copy-files/cgi-bin/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 -u 2 | 3 | from sys import stdout 4 | import time 5 | 6 | import sys, time 7 | 8 | 9 | print('Content-type: text/html\r\n\r') 10 | # print("Content-type:text/plain;charset=utf-8\n\n") 11 | 12 | 13 | import cgi, cgitb 14 | import os, sys 15 | import subprocess 16 | 17 | sys.stdout.flush() 18 | 19 | # def bash_command(cmd): 20 | # subprocess.Popen(['/bin/bash', '-c', cmd]).wait() 21 | 22 | form = cgi.FieldStorage() 23 | 24 | if "textcontent" not in form: 25 | sys.stdout.write("Can't find text content") 26 | sys.stdout.flush() 27 | f = open("/production/www/cgi-bin/links.txt", "w+") 28 | f.write("EMPTY") 29 | f.close() 30 | 31 | else: 32 | # print("

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] ..... - .flac ....." 59 | # Split string: [ .......... [SUCCESS] , - - .flac .....] 60 | # index 0 index 1 61 | # All songs are at index one 62 | songDetails = successString.split('[SUCCESS]')[1] 63 | 64 | # [... - - .flac ....] 65 | # songDetails = songDetails.split('-') 66 | 67 | # # song number is at index 0 68 | # songNum = songDetails[0].strip() 69 | 70 | # # artist is at index 1 71 | # artist = songDetails[1].strip() 72 | 73 | # # song is at index 2 74 | # song = songDetails[2].split('.flac', 1)[0].strip() 75 | 76 | # print("COMPLETED:", songNum, song, "By:", artist, "\n") 77 | print("COMPLETED:" , songDetails.strip().strip("="), "
\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("(?Phttps?://[^\s]+)", string_output).group("url") 116 | print("Go to this link to log in
\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("

") 178 | sys.exit() 179 | # print("You need to use docker cli and run command: docker exec -it tidal-dl ./tidal-login.sh
\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("

") 197 | sys.exit() 198 | if x == 3: 199 | sys.stdout.write("Error: timeout
\n") 200 | except pexpect.EOF: 201 | print("EOF error
\n") 202 | print(tidal.before) 203 | print(tidal.after) 204 | # sys.stdout.flush() 205 | except pexpect.TIMEOUT: 206 | print("Timeout Error
\n") 207 | print(ansi_escape.sub(b'',tidal.before).decode("utf-8")) 208 | print(ansi_escape.sub(b'',tidal.after).decode("utf-8")) 209 | # sys.stdout.flush() 210 | 211 | print("
\n-----------COPLETED ALL SONGS------------------
\n") 212 | print("Total number of songs:", totalSongCount, "
\n") 213 | queue.close() 214 | 215 | tidal.kill(0) 216 | print("

") 217 | sys.exit() -------------------------------------------------------------------------------- /copy-files/lib/hoge.py: -------------------------------------------------------------------------------- 1 | """test script.""" 2 | import platform 3 | 4 | def hoge(): 5 | versions = platform.python_version_tuple() 6 | return "

Python{}.{}

".format(versions[0], versions[1]) 7 | -------------------------------------------------------------------------------- /copy-files/requierments.txt: -------------------------------------------------------------------------------- 1 | pexpect==4.8.0 2 | tidal-dl==2022.10.31.1 -------------------------------------------------------------------------------- /copy-files/tidal-login.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | tidal-dl 3 | echo "Moving login token to proper directory" 4 | cp ~/.tidal-dl.token.json /production/www/cgi-bin/.tidal-dl.token.json 5 | echo "Changing file permission for tidal-dl access" 6 | chmod 666 /production/www/cgi-bin/.tidal-dl.token.json -------------------------------------------------------------------------------- /copy-files/webpage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | 27 | 28 | 29 | 30 |

Welcome to Tidal-Dl

31 |

Tidal-Dl for Web!

32 |

Insert your links, one link per line on the left

33 |
34 |
35 |
36 | 39 | 40 |
41 |
42 |
43 | 45 |
46 |
47 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | tidal-dl: 5 | container_name: tidal-local 6 | image: local/tidal-local 7 | ports: 8 | - "8885:80" 9 | volumes: 10 | - 'C:\Users\rgnet\Music\download:/production/www/cgi-bin/download/' 11 | - 'C:\Users\rgnet\Documents\github\tidal-dl-wrapper\tmp\:/production/www/cgi-bin/configuration/' 12 | -------------------------------------------------------------------------------- /logos/tidal-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgnet1/tidal-dl/d7bc4aec00423eaed50d4310dbe8fc43cf63220d/logos/tidal-icon.png -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | 1. When updating tidal-dl you need to verify the default API key options in tidal.py 2 | change: return os.path._getfullpathname("./") to os.path.abspath("./") 3 | 2. use "pip3 show tidal-dl" to get info on tidal-dl's installation 4 | 3. docker build: 5 | 6 | docker build . -t local/tidal-local -------------------------------------------------------------------------------- /public_html/test.html: -------------------------------------------------------------------------------- 1 |

hello!

2 | world 3 | --------------------------------------------------------------------------------