├── Qbitt.ipynb └── README.md /Qbitt.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Qbitt", 7 | "version": "0.3.2", 8 | "provenance": [], 9 | "collapsed_sections": [ 10 | "AHy7qI3mrjhv", 11 | "4E-uzYrv5o0B", 12 | "fkaNFMaxHDWz", 13 | "8uUbyNZHi8a5", 14 | "X_o8umGH27Ke", 15 | "6I2huqscJEPn", 16 | "5-5ksQslqmYy" 17 | ], 18 | "toc_visible": true, 19 | "include_colab_link": true 20 | }, 21 | "kernelspec": { 22 | "name": "python3", 23 | "display_name": "Python 3" 24 | }, 25 | "accelerator": "GPU" 26 | }, 27 | "cells": [ 28 | { 29 | "cell_type": "markdown", 30 | "metadata": { 31 | "id": "view-in-github", 32 | "colab_type": "text" 33 | }, 34 | "source": [ 35 | "\"Open" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": { 41 | "id": "HGctdySsIGFS", 42 | "colab_type": "text" 43 | }, 44 | "source": [ 45 | "#### 📚 For more information please visit our [GitHub](https://github.com/MinorMole/RcloneLab/).\n" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": { 51 | "colab_type": "text", 52 | "id": "AHy7qI3mrjhv" 53 | }, 54 | "source": [ 55 | "# Mount with Google Drive module\n", 56 | "#### (NOT RECOMMENDED, SOMETIMES WRITING ISSUES)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "metadata": { 62 | "colab_type": "code", 63 | "id": "BAazwoL_rjhw", 64 | "cellView": "form", 65 | "colab": {} 66 | }, 67 | "source": [ 68 | "#@ Check enabled if you want to run this cell.\n", 69 | "enabled = False #@param {type:\"boolean\"}\n", 70 | "\n", 71 | "if enabled:\n", 72 | " # Run this cell to mount your Google Drive.\n", 73 | " from google.colab import drive\n", 74 | " drive.mount('/content/drive')\n", 75 | "else:\n", 76 | " print(\"Not enabled. Skipping....\")" 77 | ], 78 | "execution_count": 0, 79 | "outputs": [] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": { 84 | "id": "4E-uzYrv5o0B", 85 | "colab_type": "text" 86 | }, 87 | "source": [ 88 | "#Mount with Rclone \n", 89 | "#### (RECOMMENDED)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "metadata": { 95 | "id": "iroeLPFdXbif", 96 | "colab_type": "code", 97 | "cellView": "form", 98 | "colab": {} 99 | }, 100 | "source": [ 101 | "#@markdown

📝 Note: Run this before using Rclone.

\n", 102 | "#@markdown

📝 Upload an rclone profile\n", 103 | "Setup_Time_Zone = False \n", 104 | "\n", 105 | "import os; from google.colab import files; from IPython.display import HTML, clear_output\n", 106 | "\n", 107 | "def upload_conf():\n", 108 | " try:\n", 109 | " display(HTML(\"

Please upload the config file of rclone (rclone.conf) from your computer.


\"))\n", 110 | " UploadConfig = files.upload().keys()\n", 111 | " clear_output(wait=True)\n", 112 | " if len(UploadConfig) == 0:\n", 113 | " return display(HTML(\"

File upload has been cancelled during upload file.


\"))\n", 114 | " elif len(UploadConfig) == 1:\n", 115 | " for fn in UploadConfig:\n", 116 | " if os.path.isfile(\"/content/\" + fn) == True:\n", 117 | " os.environ[\"rclone_conf\"] = fn\n", 118 | " !mv -f \"$rclone_conf\" /root/.rclone.conf\n", 119 | " !chmod 666 /root/.rclone.conf\n", 120 | " if Setup_Time_Zone == True:\n", 121 | " !sudo dpkg-reconfigure tzdata\n", 122 | " clear_output(wait=True)\n", 123 | " if os.path.isfile(\"/usr/bin/rclone\") == True:\n", 124 | " return display(HTML(\"

Config has been changed.


\"))\n", 125 | " else:\n", 126 | " !rm -rf /content/sample_data/\n", 127 | " !curl -s https://rclone.org/install.sh | sudo bash\n", 128 | " clear_output(wait=True)\n", 129 | " return display(HTML(\"

Installation has been successfully completed.


\"))\n", 130 | " else:\n", 131 | " return display(HTML(\"

File upload has been failed during upload file.


\"))\n", 132 | " else:\n", 133 | " for fn in UploadConfig:\n", 134 | " os.environ[\"rclone_conf\"] = fn\n", 135 | " !rm -f \"$rclone_conf\"\n", 136 | " return display(HTML(\"

Please uploading only one file at a time.


\"))\n", 137 | " except:\n", 138 | " clear_output(wait=True)\n", 139 | " return display(HTML(\"

Error occurred during upload file.


\"))\n", 140 | "\n", 141 | "upload_conf()" 142 | ], 143 | "execution_count": 0, 144 | "outputs": [] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "metadata": { 149 | "id": "mp6XoynaX4HN", 150 | "colab_type": "code", 151 | "cellView": "form", 152 | "colab": {} 153 | }, 154 | "source": [ 155 | "# ============================= FORM ============================= #\n", 156 | "#@markdown

📝 Run this to mount the drive with rclone

\n", 157 | "rclone_config_name = \"uploaddrive\" #@param {type:\"string\"}\n", 158 | "local_mount_location = \"/content/udrive/\" #@param {type:\"string\"}\n", 159 | "#@markdown The default qBittorrent download location is '/content/udrive/' (they get downloaded to '/content/qbittorrent/' first)\n", 160 | "# ============================= FORM ============================= #\n", 161 | "import time\n", 162 | "import os\n", 163 | "\n", 164 | "# clear nohup\n", 165 | "open(\"nohup.out\", 'w').close()\n", 166 | "\n", 167 | "\n", 168 | "# unmount first\n", 169 | "\n", 170 | "!fusermount -u $local_mount_location 2>/dev/null\n", 171 | "\n", 172 | "\n", 173 | "# mount without waiting for the command to complete\n", 174 | "!mkdir $local_mount_location 2>/dev/null\n", 175 | "!nohup rclone mount $rclone_config_name: $local_mount_location --buffer-size 96M & \n", 176 | " \n", 177 | "\n", 178 | "\n", 179 | "# Show the output that was written to nohup\n", 180 | "time.sleep(3)\n", 181 | "f = open(r\"nohup.out\", \"r\")\n", 182 | "nohupText = f.read()\n", 183 | "f.close()\n", 184 | "\n", 185 | "\n", 186 | "dirs = os.listdir(local_mount_location)\n", 187 | "\n", 188 | "if len(dirs) > 0:\n", 189 | " clear_output(wait=True)\n", 190 | " print(\"Succeeded. \", str(len(dirs)), \"dirs found at\", local_mount_location)\n", 191 | "\n", 192 | "else:\n", 193 | " print(\"\\n\\nNot succeeded. No files or directories in mounted location. \\nCheck your config name and content. If the rclone command was not found, run the cell above.\\n\\n\")\n", 194 | " print(\"log:\\n\", nohupText)" 195 | ], 196 | "execution_count": 0, 197 | "outputs": [] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": { 202 | "id": "fkaNFMaxHDWz", 203 | "colab_type": "text" 204 | }, 205 | "source": [ 206 | "# \"qBittorrent\"/" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "metadata": { 212 | "id": "pADE2jSHGofX", 213 | "colab_type": "code", 214 | "cellView": "form", 215 | "colab": {} 216 | }, 217 | "source": [ 218 | "# ============================= FORM ============================= #\n", 219 | "#@markdown

📝CREATE CONFIG FILE

\n", 220 | "connections = 450 #@param {type:\"integer\"}\n", 221 | "savePath = \"/content/udrive/\" #@param {type:\"string\"}\n", 222 | "install_search_plugins = True #@param {type:'boolean'}\n", 223 | "#https://github.com/thim0o/Google-Colab-Peerflix-Server\n", 224 | "# ============================= FORM ============================= #\n", 225 | "\n", 226 | "import os\n", 227 | "import logging\n", 228 | "import json\n", 229 | "import requests\n", 230 | "import ast\n", 231 | "\n", 232 | "configPath = r\"/root/.config/qBittorrent/qBittorrent.conf\"\n", 233 | "\n", 234 | "\n", 235 | "#CREATE THE CONFIG\n", 236 | "data = r\"\"\"\n", 237 | "[AutoRun][AutoRun]\n", 238 | "enabled=true\n", 239 | "program=chmod -R 666 \\\"%D\\\"\n", 240 | "\n", 241 | "[BitTorrent]\n", 242 | "Session\\CreateTorrentSubfolder=true\n", 243 | "Session\\DisableAutoTMMByDefault=false\n", 244 | "Session\\DisableAutoTMMTriggers\\CategoryChanged=false\n", 245 | "Session\\DisableAutoTMMTriggers\\CategorySavePathChanged=false\n", 246 | "Session\\DisableAutoTMMTriggers\\DefaultSavePathChanged=true\n", 247 | "Session\\GlobalMaxSeedingMinutes=1000\n", 248 | "\n", 249 | "[Core]\n", 250 | "AutoDeleteAddedTorrentFile=IfAdded\n", 251 | "\n", 252 | "[Preferences]\n", 253 | "Downloads\\DiskWriteCacheSize=64\n", 254 | "Session\\SuggestMode=true\n", 255 | "Advanced\\AnnounceToAllTrackers=true\n", 256 | "Session\\AnnounceToAllTiers=true\n", 257 | "Bittorrent\\AddTrackers=true\n", 258 | "Bittorrent\\MaxConnecs={connections}\n", 259 | "Bittorrent\\MaxConnecsPerTorrent=-1\n", 260 | "Bittorrent\\MaxRatio=4\n", 261 | "Bittorrent\\MaxRatioAction=0\n", 262 | "Bittorrent\\MaxUploads=8\n", 263 | "Bittorrent\\PeX=true\n", 264 | "Bittorrent\\TrackersList=http://nyaa.tracker.wf:7777/announce\\n\\nhttp://sukebei.tracker.wf:8888/announce\\n\\nudp://tracker.coppersurfer.tk:6969/announce\\n\\nudp://tracker.open-internet.nl:6969/announce\\n\\nudp://tracker.leechers-paradise.org:6969/announce\\n\\nudp://tracker.internetwarriors.net:1337/announce\\n\\nudp://tracker.opentrackr.org:1337/announce\\n\\nudp://9.rarbg.to:2710/announce\\n\\nudp://9.rarbg.me:2710/announce\\n\\nhttp://tracker3.itzmx.com:6961/announce\\n\\nhttp://tracker1.itzmx.com:8080/announce\\n\\nudp://exodus.desync.com:6969/announce\\n\\nudp://explodie.org:6969/announce\\n\\nudp://ipv4.tracker.harry.lu:80/announce\\n\\nudp://denis.stalker.upeer.me:6969/announce\\n\\nudp://tracker.torrent.eu.org:451/announce\\n\\nudp://tracker.tiny-vps.com:6969/announce\\n\\nudp://thetracker.org:80/announce\\n\\nudp://open.demonii.si:1337/announce\\n\\nudp://tracker4.itzmx.com:2710/announce\\n\\nudp://tracker.cyberia.is:6969/announce\\n\\nudp://retracker.netbynet.ru:2710/announce\\n\\nudp://62.138.0.158:6969/announce\\n\\nudp://188.241.58.209:6969/announce\\n\\nudp://188.241.58.209:6969/announce\\n\\nudp://185.225.17.100:1337/announce\\n\\nudp://62.210.79.110:1337/announce\\n\\nudp://151.80.120.112:2710/announce\\n\\nudp://151.80.120.114:2710/announce\\n\\nhttp://176.113.71.19:6961/announce\\n\\nhttp://172.64.195.37:8080/announce\\n\\nudp://208.83.20.20:6969/announce\\n\\nudp://184.105.151.164:6969/announce\\n\\nudp://51.15.40.114:80/announce\\n\\nudp://5.2.79.22:6969/announce\\n\\nudp://91.216.110.52:451/announce\\n\\nudp://5.206.58.23:6969/announce\\n\\nudp://176.31.106.35:80/announce\\n\\nudp://5.2.79.219:1337/announce\\n\\nudp://212.1.226.176:2710/announce\\n\\nudp://159.100.245.181:6969/announce\\n\\nudp://212.1.226.176:2710/announce\\n\n", 265 | "Bittorrent\\uTP_rate_limited=false\n", 266 | "Connection\\GlobalDLLimitAlt=0\n", 267 | "Connection\\GlobalUPLimit=30000\n", 268 | "Connection\\GlobalUPLimitAlt=0\n", 269 | "Connection\\PortRangeMin=12121\n", 270 | "Downloads\\PreAllocation=false\n", 271 | "Downloads\\SavePath={savePath}\n", 272 | "Downloads\\ScanDirsV2=@Variant(\\0\\0\\0\\x1c\\0\\0\\0\\x1\\0\\0\\0\\x12\\0/\\0\\x63\\0o\\0n\\0t\\0\\x65\\0n\\0t\\0/\\0\\0\\0\\x2\\0\\0\\0\\x1)\n", 273 | "Downloads\\StartInPause=false\n", 274 | "Downloads\\TempPath=/content/qBittorrent/\n", 275 | "Downloads\\TempPathEnabled=true\n", 276 | "General\\Locale=en\n", 277 | "General\\UseRandomPort=false\n", 278 | "Queueing\\IgnoreSlowTorrents=true\n", 279 | "Queueing\\MaxActiveDownloads=8\n", 280 | "Queueing\\MaxActiveTorrents=15\n", 281 | "Queueing\\MaxActiveUploads=8\n", 282 | "Queueing\\QueueingEnabled=true\n", 283 | "WebUI\\Address=*\n", 284 | "WebUI\\AlternativeUIEnabled=false\n", 285 | "WebUI\\AuthSubnetWhitelist=@Invalid()\n", 286 | "WebUI\\AuthSubnetWhitelistEnabled=false\n", 287 | "WebUI\\ClickjackingProtection=true\n", 288 | "WebUI\\HTTPS\\Enabled=false\n", 289 | "WebUI\\HostHeaderValidation=true\n", 290 | "WebUI\\LocalHostAuth=false\n", 291 | "WebUI\\Password_ha1=@ByteArray(322517f6a6fc65697957a1f9da700b43)\n", 292 | "WebUI\\Port=6006\n", 293 | "WebUI\\RootFolder=\n", 294 | "WebUI\\Username=rclonelab\n", 295 | "\"\"\".format(connections=connections,\n", 296 | " savePath=savePath,\n", 297 | " ).split(\"\\n\")\n", 298 | "\n", 299 | "\n", 300 | "\n", 301 | "\n", 302 | "# SAVE THE CONFIG\n", 303 | "!mkdir /root/.config/qBittorrent/\n", 304 | "open(configPath, 'w').close()\n", 305 | "\n", 306 | "configFile = open(configPath, \"a+\")\n", 307 | "\n", 308 | "for line in data:\n", 309 | " configFile.write(line + \"\\n\")\n", 310 | " \n", 311 | "configFile.close()\n", 312 | "\n", 313 | "\n", 314 | "# PRINT THE CONFIG FOR DEBUGGING\n", 315 | "f = open(configPath, \"r\")\n", 316 | "text = f.read()\n", 317 | "f.close()\n", 318 | "\n", 319 | "clear_output(wait=True)\n", 320 | "print(\"created the config successfully\")\n", 321 | "\n", 322 | "f = open(r\"nohup.out\", \"w+\").close()\n", 323 | "# Download search engines\n", 324 | "\n", 325 | "engineDirectory = r\"/root/.local/share/data/qBittorrent/nova3/engines/\"\n", 326 | "if install_search_plugins:\n", 327 | " \n", 328 | " !mkdir $engineDirectory >/dev/null\n", 329 | " pluginsInstalled = 0\n", 330 | " searchEngineLinks = [r\"https://raw.githubusercontent.com/MaurizioRicci/qBittorrent_search_engine/master/kickass_torrent.py\",\n", 331 | " r\"https://raw.githubusercontent.com/MaurizioRicci/qBittorrent_search_engine/master/extratorrent.py\",\n", 332 | " r\"https://raw.githubusercontent.com/MaurizioRicci/qBittorrent_search_engine/master/ettv.py\",\n", 333 | " r\"https://raw.githubusercontent.com/hannsen/qbittorrent_search_plugins/master/demonoid.py\",\n", 334 | " r\"https://raw.githubusercontent.com/MaurizioRicci/qBittorrent_search_engine/master/torrentproject.py\",\n", 335 | " r\"https://raw.githubusercontent.com/Pireo/hello-world/master/rutor.py\",\n", 336 | " r\"https://raw.githubusercontent.com/nindogo/qbtSearchScripts/master/magnetdl.py\",\n", 337 | " r\"https://github.com/qbittorrent/search-plugins/blob/master/nova3/engines/zooqle.py\",\n", 338 | " r\"https://github.com/qbittorrent/search-plugins/blob/master/nova3/engines/rarbg.py\",\n", 339 | " r\"https://github.com/qbittorrent/search-plugins/blob/master/nova3/engines/torlock.py\",\n", 340 | " r\"https://github.com/qbittorrent/search-plugins/blob/master/nova3/engines/btdb.py\",\n", 341 | " \n", 342 | " ]\n", 343 | " cmd = \"\"\n", 344 | " for link in searchEngineLinks:\n", 345 | " !nohup wget -nc -P /root/.local/share/data/qBittorrent/nova3/engines/ $link >/dev/null 2>&1 &\n", 346 | " pluginsInstalled += 1\n", 347 | " clear_output(wait=True)\n", 348 | " !$cmd\n", 349 | "\n", 350 | " print(pluginsInstalled, \"plugins installed\")\n", 351 | "\n", 352 | "f = open(r\"nohup.out\", \"r\")\n", 353 | "nohupText = f.read()\n", 354 | "print(nohupText)\n", 355 | "\n", 356 | "dirs = os.listdir(engineDirectory)\n", 357 | "print(len(dirs), \"in plugins folder\")\n" 358 | ], 359 | "execution_count": 0, 360 | "outputs": [] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "metadata": { 365 | "id": "ELoHkXE1Xnms", 366 | "colab_type": "code", 367 | "cellView": "form", 368 | "colab": {} 369 | }, 370 | "source": [ 371 | "# ============================= FORM ============================= #\n", 372 | "#@markdown

📝 INSTALL QBITTORRENT AND FILE MANAGER

\n", 373 | "Old_Version = False #@param {type:\"boolean\"}\n", 374 | "Install_File_Manager = True #@param {type:\"boolean\"}\n", 375 | "subdomain = \"random\" #@param {type:\"string\"}\n", 376 | "\n", 377 | "\n", 378 | "# ================================================================ #\n", 379 | "\n", 380 | "import os, psutil, IPython, uuid, time\n", 381 | "import ipywidgets as widgets\n", 382 | "\n", 383 | "from IPython.display import HTML, clear_output\n", 384 | "from google.colab import output\n", 385 | "\n", 386 | "#kill qbittorrent to be able to reload config\n", 387 | "!pkill qbittorrent-nox\n", 388 | "!pkill autossh\n", 389 | "SuccessRun = widgets.Button(\n", 390 | " description='✔ Successfully',\n", 391 | " disabled=True,\n", 392 | " button_style='success'\n", 393 | ")\n", 394 | "\n", 395 | "UnsuccessfullyRun = widgets.Button(\n", 396 | " description='✘ Unsuccessfully',\n", 397 | " disabled=True,\n", 398 | " button_style='danger'\n", 399 | ")\n", 400 | "\n", 401 | "class MakeButton(object):\n", 402 | " def __init__(self, title, callback):\n", 403 | " self._title = title\n", 404 | " self._callback = callback\n", 405 | " def _repr_html_(self):\n", 406 | " callback_id = 'button-' + str(uuid.uuid4())\n", 407 | " output.register_callback(callback_id, self._callback)\n", 408 | " template = \"\"\"\n", 409 | " \"\"\"\n", 415 | " html = template.format(title=self._title, callback_id=callback_id)\n", 416 | " return html\n", 417 | "\n", 418 | "def RandomGenerator():\n", 419 | " return time.strftime(\"%S\") + str(time.time()).split(\".\")[-1]\n", 420 | "\n", 421 | "def CheckProcess(process, command):\n", 422 | " for pid in psutil.pids():\n", 423 | " try:\n", 424 | " p = psutil.Process(pid)\n", 425 | " if process in p.name():\n", 426 | " for arg in p.cmdline():\n", 427 | " if command in str(arg): \n", 428 | " return True\n", 429 | " else:\n", 430 | " pass\n", 431 | " else:\n", 432 | " pass\n", 433 | " except:\n", 434 | " continue\n", 435 | " \n", 436 | "def AutoSSH(name,port):\n", 437 | " get_ipython().system_raw(\"autossh -l \" + name + \" -M 0 -fNT -o 'StrictHostKeyChecking=no' -o 'ServerAliveInterval 300' -o 'ServerAliveCountMax 30' -R 80:localhost:\" + port + \" ssh.localhost.run &\")\n", 438 | " get_ipython().system_raw(\"autossh -M 0 -fNT -o 'StrictHostKeyChecking=no' -o 'ServerAliveInterval 300' -o 'ServerAliveCountMax 30' -R \" + name + \":80:localhost:\" + port + \" serveo.net &\")\n", 439 | "\n", 440 | "def Start_Server():\n", 441 | " if CheckProcess(\"qbittorrent-nox\", \"\") != True:\n", 442 | " print(\"Starting qbittorrent...\")\n", 443 | " !qbittorrent-nox -d --webui-port=6006\n", 444 | " if CheckProcess(\"autossh\", Random_Number) != True:\n", 445 | " AutoSSH(Random_Number, \"6006\")\n", 446 | " if Install_File_Manager == True:\n", 447 | " if os.path.isfile(\"/tools/node/bin/cloudcmd\") == False:\n", 448 | " clear_output(wait=True)\n", 449 | " !npm i -g npm\n", 450 | " !npm i cloudcmd -g\n", 451 | " if CheckProcess(\"cloudcmd\", \"\") != True:\n", 452 | " get_ipython().system_raw(\"cloudcmd --online --no-auth --show-config --show-file-name --editor 'deepword' --packer 'tar' --port 7007 --progress --no-confirm-copy --confirm-move --name 'RcloneLab File Manager' --keys-panel --no-contact --console --sync-console-path --no-terminal --no-vim --columns 'name-size-date' --no-log &\")\n", 453 | " if CheckProcess(\"autossh\", \"fm\" + Random_Number) != True:\n", 454 | " AutoSSH(\"fm\" + Random_Number, \"7007\")\n", 455 | "\n", 456 | "\n", 457 | "\n", 458 | "try:\n", 459 | " if subdomain == \"random\":\n", 460 | " \n", 461 | " Random_NumberMT = RandomGenerator()\n", 462 | " Random_Number = RandomGenerator()\n", 463 | " else:\n", 464 | " Random_NumberMT = subdomain\n", 465 | " Random_Number = subdomain\n", 466 | "\n", 467 | "# !rm -rf /content/{sample_data,.config}/\n", 468 | " \n", 469 | " if os.path.isfile(\"/usr/bin/qbittorrent-nox\") == False:\n", 470 | " !apt update -qq -y\n", 471 | " !yes \"\" | add-apt-repository ppa:qbittorrent-team/qbittorrent-stable\n", 472 | " if Old_Version == True:\n", 473 | " !apt install qbittorrent-nox=4.0.3-1 -qq -y\n", 474 | " else:\n", 475 | " !apt install qbittorrent-nox -qq -y\n", 476 | " \n", 477 | " clear_output(wait=True)\n", 478 | "\n", 479 | " if os.path.isfile(\"/usr/bin/autossh\") == False:\n", 480 | " !apt install autossh -qq -y\n", 481 | " Start_Server()\n", 482 | " display(SuccessRun)\n", 483 | " display(MakeButton(\"Recheck\", Start_Server))\n", 484 | " display(HTML(\"

qBittorrent

Website 1
\" \\\n", 485 | " \"Website 2

\"))\n", 486 | " if Install_File_Manager == True:\n", 487 | " display(HTML(\n", 488 | " \"

File Manager

Website 1
\" \"Website 2

\"))\n", 489 | "except Exception as e:\n", 490 | " console.log(e)\n", 491 | " display(UnsuccessfullyRun)" 492 | ], 493 | "execution_count": 0, 494 | "outputs": [] 495 | }, 496 | { 497 | "cell_type": "markdown", 498 | "metadata": { 499 | "id": "8uUbyNZHi8a5", 500 | "colab_type": "text" 501 | }, 502 | "source": [ 503 | "# \"JDownloader\"/" 504 | ] 505 | }, 506 | { 507 | "cell_type": "code", 508 | "metadata": { 509 | "colab_type": "code", 510 | "cellView": "both", 511 | "id": "LqVdyGLU9g3A", 512 | "colab": {} 513 | }, 514 | "source": [ 515 | "#@markdown

⬅️ Click Here to Install JDownloader

\n", 516 | "\n", 517 | "import os, uuid, re, IPython\n", 518 | "import ipywidgets as widgets\n", 519 | "\n", 520 | "from glob import glob\n", 521 | "from IPython.display import HTML, clear_output\n", 522 | "from google.colab import output\n", 523 | "\n", 524 | "Email = widgets.Text(placeholder=\"*Required\", description=\"Email:\")\n", 525 | "Password = widgets.Text(placeholder=\"*Required\", description=\"Password:\")\n", 526 | "Device = widgets.Text(placeholder=\"Optional\", description=\"Name:\")\n", 527 | "SavePath = widgets.Text(placeholder=\"/content/Downloads\", description=\"Save Path:\")\n", 528 | "\n", 529 | "class MakeButton(object):\n", 530 | " def __init__(self, title, callback, style):\n", 531 | " self._title = title\n", 532 | " self._callback = callback\n", 533 | " self._style = style\n", 534 | " def _repr_html_(self):\n", 535 | " callback_id = 'button-' + str(uuid.uuid4())\n", 536 | " output.register_callback(callback_id, self._callback)\n", 537 | " if self._style != \"\":\n", 538 | " style_html = \"p-Widget jupyter-widgets jupyter-button widget-button mod-\" + self._style\n", 539 | " else:\n", 540 | " style_html = \"p-Widget jupyter-widgets jupyter-button widget-button\"\n", 541 | " template = \"\"\"\n", 542 | " \"\"\"\n", 548 | " html = template.format(title=self._title, callback_id=callback_id, style_html=style_html)\n", 549 | " return html\n", 550 | " \n", 551 | "def MakeLabel(description, button_style):\n", 552 | " return widgets.Button(description=description, disabled=True, button_style=button_style)\n", 553 | "\n", 554 | "def RefreshPath():\n", 555 | " if os.path.exists(\"/content/drive/\"):\n", 556 | " if os.path.exists(\"/content/drive/Shared drives/\"):\n", 557 | " SavePath.options = [\"/content\", \"/content/Downloads\", \"/content/drive/My Drive\"] + glob(\"/content/drive/My Drive/*/\") + glob(\"/content/drive/Shared drives/*/\")\n", 558 | " else:\n", 559 | " SavePath.options = [\"/content\", \"/content/Downloads\", \"/content/drive/My Drive\"] + glob(\"/content/drive/My Drive/*/\")\n", 560 | " else:\n", 561 | " SavePath.options = [\"/content\", \"/content/Downloads\"]\n", 562 | "\n", 563 | "def LoginForm():\n", 564 | " clear_output(wait=True)\n", 565 | " Email.value = \"\"\n", 566 | " Password.value = \"\"\n", 567 | " Device.value = \"\"\n", 568 | " RefreshPath()\n", 569 | " display(HTML(\"

If you don't have an account yet, please register here.

\"), HTML(\"
\"), Email, Password, Device, SavePath, MakeButton(\"Refresh\", RefreshPath, \"\"))\n", 570 | " if not os.path.exists(\"/content/drive/\"):\n", 571 | " display(HTML(\"\"))\n", 572 | " display(HTML(\"
\"), MakeButton(\"Login\", CheckLogin, \"info\"))\n", 573 | " if os.path.isfile(\"/root/.JDownloader/cfg/org.jdownloader.api.myjdownloader.MyJDownloaderSettings.json\"):\n", 574 | " display(MakeButton(\"Cancel\", Show, \"danger\"))\n", 575 | " \n", 576 | "def RestartForm():\n", 577 | " clear_output(wait=True)\n", 578 | " display(MakeLabel(\"Restart Confirm?\", \"\"), MakeButton(\"Confirm\", Restart, \"danger\"), MakeButton(\"Cancel\", Show, \"warning\"))\n", 579 | " \n", 580 | "def ExitForm():\n", 581 | " clear_output(wait=True)\n", 582 | " display(MakeLabel(\"Exit Confirm?\", \"\"), MakeButton(\"Confirm\", Exit, \"danger\"), MakeButton(\"Cancel\", Show, \"warning\"))\n", 583 | "\n", 584 | "def CheckLogin():\n", 585 | " try:\n", 586 | " if not Email.value.strip():\n", 587 | " ERROR = \"Email field is empty.\"\n", 588 | " THROW_ERROR\n", 589 | " if not \"@\" in Email.value and not \".\" in Email.value:\n", 590 | " ERROR = \"Email is an incorrect format.\"\n", 591 | " THROW_ERROR\n", 592 | " if not Password.value.strip():\n", 593 | " ERROR = \"Password field is empty.\"\n", 594 | " THROW_ERROR\n", 595 | " if not bool(re.match(\"^[a-zA-Z0-9]+$\", Device.value)) and Device.value.strip():\n", 596 | " ERROR = \"Only alphanumeric are allowed for the device name.\"\n", 597 | " THROW_ERROR\n", 598 | " Login()\n", 599 | " except:\n", 600 | " print(ERROR)\n", 601 | "\n", 602 | "def Login():\n", 603 | " clear_output(wait=True)\n", 604 | " if SavePath.value == \"/content\":\n", 605 | " get_ipython().system_raw(\"echo '{\\\"defaultdownloadfolder\\\" : \\\"/content\\\"}' > /root/.JDownloader/cfg/org.jdownloader.settings.GeneralSettings.json\")\n", 606 | " elif SavePath.value == \"/content/Downloads\":\n", 607 | " get_ipython().system_raw(\"mkdir -p -m 666 /content/Downloads\")\n", 608 | " get_ipython().system_raw(\"echo '{\\\"defaultdownloadfolder\\\" : \\\"/content/Downloads\\\"}' > /root/.JDownloader/cfg/org.jdownloader.settings.GeneralSettings.json\")\n", 609 | " else:\n", 610 | " get_ipython().system_raw(\"echo '{\\\"defaultdownloadfolder\\\" : \\\"\" + SavePath.value + \"\\\"}' > /root/.JDownloader/cfg/org.jdownloader.settings.GeneralSettings.json\")\n", 611 | " if Device.value.strip() == \"\":\n", 612 | " Device.value = Email.value\n", 613 | " get_ipython().system_raw(\"pkill -9 -e -f java\")\n", 614 | " get_ipython().system_raw(\"echo '{\\\"email\\\" : \\\"'\" + Email.value + \"'\\\", \\\"password\\\" : \\\"'\" + Password.value + \"'\\\", \\\"devicename\\\" : \\\"'\" + Device.value + \"'\\\", \\\"directconnectmode\\\" : \\\"LAN\\\"}' > /root/.JDownloader/cfg/org.jdownloader.api.myjdownloader.MyJDownloaderSettings.json\")\n", 615 | " get_ipython().system_raw(\"java -jar /root/.JDownloader/JDownloader.jar -norestart -noerr -r &\")\n", 616 | " Show()\n", 617 | "\n", 618 | "def Restart():\n", 619 | " get_ipython().system_raw(\"pkill -9 -e -f java\")\n", 620 | " get_ipython().system_raw(\"java -jar /root/.JDownloader/JDownloader.jar -norestart -noerr -r &\")\n", 621 | " Show()\n", 622 | " \n", 623 | "def Exit():\n", 624 | " get_ipython().system_raw(\"pkill -9 -e -f java\")\n", 625 | " clear_output(wait=True)\n", 626 | " display(MakeButton(\"Start\", Restart, \"info\"))\n", 627 | " \n", 628 | "def Show():\n", 629 | " clear_output(wait=True)\n", 630 | " display(MakeLabel(\"Control Panel\", \"\"), HTML(\"

You can login to the WebUI by clicking here.

\"), HTML(\"

If the server didn't showup in 30 sec. please re-login.

\"), HTML(\"
\"), MakeButton(\"Re-Login\", LoginForm, \"info\"), MakeButton(\"Restart\", RestartForm, \"warning\"), MakeButton(\"Exit\", ExitForm, \"danger\"))\n", 631 | " \n", 632 | " \n", 633 | "if not os.path.isfile(\"/root/.JDownloader/JDownloader.jar\"):\n", 634 | " clear_output(wait=True)\n", 635 | " display(MakeLabel(\"Installing in Progress\", \"warning\"))\n", 636 | " get_ipython().system_raw(\"rm -rf /content/sample_data/ && apt install openjdk-8-jre-headless -qq -y && mkdir -p -m 666 /root/.JDownloader && wget -q http://installer.jdownloader.org/JDownloader.jar -O /root/.JDownloader/JDownloader.jar && java -jar /root/.JDownloader/JDownloader.jar -norestart -h\")\n", 637 | " LoginForm()\n", 638 | "elif not os.path.isfile(\"/root/.JDownloader/cfg/org.jdownloader.api.myjdownloader.MyJDownloaderSettings.json\"):\n", 639 | " LoginForm()\n", 640 | "else: \n", 641 | " Show()" 642 | ], 643 | "execution_count": 0, 644 | "outputs": [] 645 | }, 646 | { 647 | "cell_type": "markdown", 648 | "metadata": { 649 | "id": "X_o8umGH27Ke", 650 | "colab_type": "text" 651 | }, 652 | "source": [ 653 | "# \"Youtube-DL\"/" 654 | ] 655 | }, 656 | { 657 | "cell_type": "code", 658 | "metadata": { 659 | "id": "v_bqZeo83e-S", 660 | "colab_type": "code", 661 | "cellView": "form", 662 | "colab": {} 663 | }, 664 | "source": [ 665 | "#@markdown

⬅️ Click Here to Use YouTube-DL (Beta)

\n", 666 | "\n", 667 | "import os, uuid, urllib.parse\n", 668 | "import ipywidgets as widgets\n", 669 | "\n", 670 | "from glob import glob\n", 671 | "from urllib.parse import urlparse, parse_qs\n", 672 | "from IPython.display import HTML, clear_output, YouTubeVideo\n", 673 | "from google.colab import output\n", 674 | "\n", 675 | "Links = widgets.Textarea(placeholder='''Video/Playlist Link\n", 676 | "(one link per line)''')\n", 677 | "\n", 678 | "VideoQ = widgets.Dropdown(options=[\"Best Quality (VP9 upto 4K)\", \"Best Compatibility (H.264 upto 1080p)\"], description=\"Video:\")\n", 679 | "\n", 680 | "AudioQ = widgets.Dropdown(options=[\"Best Quality (Opus)\", \"Best Compatibility (M4A)\"], description=\"Audio:\")\n", 681 | "\n", 682 | "Subtitle = widgets.Dropdown(options=[\"Enable\", \"Disable\"], description=\"Subtitle:\")\n", 683 | "\n", 684 | "SavePathYT = widgets.Dropdown(options=[\"/content\", \"/content/Downloads\"], description=\"Save Path:\")\n", 685 | "\n", 686 | "class MakeButton(object):\n", 687 | " def __init__(self, title, callback, style):\n", 688 | " self._title = title\n", 689 | " self._callback = callback\n", 690 | " self._style = style\n", 691 | " def _repr_html_(self):\n", 692 | " callback_id = 'button-' + str(uuid.uuid4())\n", 693 | " output.register_callback(callback_id, self._callback)\n", 694 | " if self._style != \"\":\n", 695 | " style_html = \"p-Widget jupyter-widgets jupyter-button widget-button mod-\" + self._style\n", 696 | " else:\n", 697 | " style_html = \"p-Widget jupyter-widgets jupyter-button widget-button\"\n", 698 | " template = \"\"\"\n", 699 | " \"\"\"\n", 705 | " html = template.format(title=self._title, callback_id=callback_id, style_html=style_html)\n", 706 | " return html\n", 707 | " \n", 708 | "def MakeLabel(description, button_style):\n", 709 | " return widgets.Button(description=description, disabled=True, button_style=button_style)\n", 710 | "\n", 711 | "def youtube_id(value):\n", 712 | " query = urlparse(value)\n", 713 | " if query.hostname == 'youtu.be':\n", 714 | " return query.path[1:]\n", 715 | " if query.hostname in ('www.youtube.com', 'youtube.com'):\n", 716 | " if query.path == '/watch':\n", 717 | " p = parse_qs(query.query)\n", 718 | " return p['v'][0]\n", 719 | " if query.path[:7] == '/embed/':\n", 720 | " return query.path.split('/')[2]\n", 721 | " if query.path[:3] == '/v/':\n", 722 | " return query.path.split('/')[2]\n", 723 | " return None\n", 724 | "\n", 725 | "def RefreshPathYT():\n", 726 | " if os.path.exists(\"/content/drive/\"):\n", 727 | " if os.path.exists(\"/content/drive/Shared drives/\"):\n", 728 | " SavePathYT.options = [\"/content\", \"/content/Downloads\", \"/content/drive/My Drive\"] + glob(\"/content/drive/My Drive/*/\") + glob(\"/content/drive/Shared drives/*/\")\n", 729 | " else:\n", 730 | " SavePathYT.options = [\"/content\", \"/content/Downloads\", \"/content/drive/My Drive\"] + glob(\"/content/drive/My Drive/*/\")\n", 731 | " else:\n", 732 | " SavePathYT.options = [\"/content\", \"/content/Downloads\"]\n", 733 | "\n", 734 | "def ShowYT():\n", 735 | " clear_output(wait=True)\n", 736 | " RefreshPathYT()\n", 737 | " display(Links, VideoQ, AudioQ, Subtitle, SavePathYT, MakeButton(\"Refresh\", RefreshPathYT, \"\"))\n", 738 | " if not os.path.exists(\"/content/drive/\"):\n", 739 | " display(HTML(\"\"))\n", 740 | " display(HTML(\"
\"), MakeButton(\"Download\", DownloadYT, \"info\"))\n", 741 | "\n", 742 | "def DownloadYT():\n", 743 | " if Links.value.strip():\n", 744 | " Count = 0\n", 745 | " Total = str(len(Links.value.splitlines()))\n", 746 | " # Video\n", 747 | " if VideoQ.value == \"Best Quality (VP9 upto 4K)\":\n", 748 | " os.environ[\"videoqC\"] = \"webm\"\n", 749 | " else:\n", 750 | " os.environ[\"videoqC\"] = \"mp4\"\n", 751 | " # Audio\n", 752 | " if AudioQ.value == \"Best Quality (Opus)\":\n", 753 | " os.environ[\"audioqC\"] = \"webm\"\n", 754 | " else:\n", 755 | " os.environ[\"audioqC\"] = \"m4a\"\n", 756 | " # Subtitle\n", 757 | " if Subtitle.value == \"Enable\":\n", 758 | " os.environ[\"subtitleC\"] = \"--all-subs --convert-subs ass --embed-subs\"\n", 759 | " else:\n", 760 | " os.environ[\"subtitleC\"] = \"\"\n", 761 | " # Compatibility Extension\n", 762 | " if Subtitle.value == \"Disable\" and AudioQ.value == \"Best Compatibility (M4A)\":\n", 763 | " os.environ[\"extC\"] = \"mp4\"\n", 764 | " os.environ[\"thumbnailC\"] = \"--embed-thumbnail\"\n", 765 | " else:\n", 766 | " os.environ[\"extC\"] = \"mkv\"\n", 767 | " os.environ[\"thumbnailC\"] = \"\"\n", 768 | " for Link in Links.value.splitlines():\n", 769 | " clear_output(wait=True)\n", 770 | " Count += 1\n", 771 | " display(HTML(\"

Processing \" + str(Count) + \" out of \" + Total + \"

\"))\n", 772 | " if youtube_id(Link) != None:\n", 773 | " display(HTML(\"

Video currently downloading...


\"), YouTubeVideo(youtube_id(Link), width=640, height=360), HTML(\"
\"))\n", 774 | " else:\n", 775 | " display(HTML(\"

Currently downloading \" + Link + \"


\"))\n", 776 | " os.environ[\"Link\"] = Link\n", 777 | " !youtube-dl -i --no-warnings --geo-bypass --yes-playlist --add-metadata --merge-output-format $extC $thumbnailC $subtitleC -f \"bestvideo[ext=$videoqC]+bestaudio[ext=$audioqC]/bestvideo+bestaudio\" -o \"/root/.YouTube-DL/%(title)s.%(ext)s\" \"$Link\"\n", 778 | " if not os.path.exists(SavePathYT.value):\n", 779 | " get_ipython().system_raw(\"mkdir -p -m 666 \" + SavePathYT.value)\n", 780 | " get_ipython().system_raw(\"mv /root/.YouTube-DL/* '\" + SavePathYT.value + \"/'\")\n", 781 | " ShowYT()\n", 782 | "\n", 783 | "if not os.path.isfile(\"/usr/local/bin/youtube-dl\"):\n", 784 | " get_ipython().system_raw(\"rm -rf /content/sample_data/ && mkdir -p -m 666 /root/.YouTube-DL/ && apt-get install atomicparsley && curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl && chmod a+rx /usr/local/bin/youtube-dl\")\n", 785 | "ShowYT()" 786 | ], 787 | "execution_count": 0, 788 | "outputs": [] 789 | }, 790 | { 791 | "cell_type": "markdown", 792 | "metadata": { 793 | "id": "6I2huqscJEPn", 794 | "colab_type": "text" 795 | }, 796 | "source": [ 797 | "# \"RcloneLab\"/" 798 | ] 799 | }, 800 | { 801 | "cell_type": "code", 802 | "metadata": { 803 | "id": "RIr3CXMAGNdy", 804 | "colab_type": "code", 805 | "cellView": "form", 806 | "colab": {} 807 | }, 808 | "source": [ 809 | "# ============================= FORM ============================= #\n", 810 | "Mode = \"Off\" #@param [\"Off\",\"Config\", \"Copy\", \"Move\", \"Sync\", \"Checker\", \"Deduplicate\", \"Remove Empty Directories\", \"Empty Trash\", \"qBittorrent\"]\n", 811 | "Compare = \"Size & Mod-Time\" #@param [\"Size & Mod-Time\", \"Size & Checksum\", \"Only Mod-Time\", \"Only Size\", \"Only Checksum\"]\n", 812 | "Source = \"\" #@param {type:\"string\"}\n", 813 | "Destination = \"uploaddrive:/test/\" #@param {type:\"string\"}\n", 814 | "Transfers = 4 #@param {type:\"slider\", min:1, max:20, step:1}\n", 815 | "Checkers = 20 #@param {type:\"slider\", min:1, max:40, step:1}\n", 816 | "#@markdown ---\n", 817 | "\n", 818 | "#@markdown

⚙️ Global Configuration ⚙️

\n", 819 | "Simple_Ouput = True #@param {type:\"boolean\"}\n", 820 | "Skip_files_that_are_newer_on_the_destination = True #@param {type:\"boolean\"}\n", 821 | "Skip_all_files_that_exist = True #@param {type:\"boolean\"}\n", 822 | "Do_not_cross_filesystem_boundaries = False\n", 823 | "Do_not_update_modtime_if_files_are_identical = False #@param {type:\"boolean\"}\n", 824 | "Large_amount_of_files_optimization = False\n", 825 | "Google_Drive_optimization = True #@param {type:\"boolean\"}\n", 826 | "Dry_Run = False #@param {type:\"boolean\"}\n", 827 | "Output_Log_File = \"DEBUG\" #@param [\"OFF\", \"NOTICE\", \"INFO\", \"ERROR\", \"DEBUG\"]\n", 828 | "Extra_Arguments = \"\" #@param {type:\"string\"}\n", 829 | "\n", 830 | "#@markdown

↪️ Sync Configuration ↩️

\n", 831 | "Sync_Mode = \"Delete during transfer\" #@param [\"Delete during transfer\", \"Delete before transfering\", \"Delete after transfering\"]\n", 832 | "Track_Renames = False #@param {type:\"boolean\"}\n", 833 | "\n", 834 | "#@markdown

💞 Deduplicate Configuration 💞

\n", 835 | "Deduplicate_Mode = \"Interactive\" #@param [\"Interactive\", \"Skip\", \"First\", \"Newest\", \"Oldest\", \"Largest\", \"Rename\"]\n", 836 | "Deduplicate_Use_Trash = True #@param {type:\"boolean\"}\n", 837 | "# ================================================================ #\n", 838 | "\n", 839 | "import os; from IPython.display import HTML, clear_output\n", 840 | "import sys\n", 841 | "\n", 842 | "\n", 843 | "if Mode != \"Off\":\n", 844 | "\n", 845 | " # Optimized for Google Colaboratory\n", 846 | " os.environ[\"bufferC\"] = \"--buffer-size 96M\"\n", 847 | "\n", 848 | " if Compare == \"Size & Checksum\":\n", 849 | " os.environ[\"compareC\"] = \"-c\"\n", 850 | " elif Compare == \"Only Mod-Time\":\n", 851 | " os.environ[\"compareC\"] = \"--ignore-size\"\n", 852 | " elif Compare == \"Only Size\":\n", 853 | " os.environ[\"compareC\"] = \"--size-only\"\n", 854 | " elif Compare == \"Only Checksum\":\n", 855 | " os.environ[\"compareC\"] = \"-c --ignore-size\"\n", 856 | " else:\n", 857 | " os.environ[\"compareC\"] = \"\"\n", 858 | "\n", 859 | " os.environ[\"sourceC\"] = Source\n", 860 | " os.environ[\"destinationC\"] = Destination\n", 861 | " os.environ[\"transfersC\"] = \"--transfers \"+str(Transfers)\n", 862 | " os.environ[\"checkersC\"] = \"--checkers \"+str(Checkers)\n", 863 | "\n", 864 | " if Skip_files_that_are_newer_on_the_destination == True:\n", 865 | " os.environ[\"skipnewC\"] = \"-u\"\n", 866 | " else:\n", 867 | " os.environ[\"skipnewC\"] = \"\"\n", 868 | "\n", 869 | " if Skip_all_files_that_exist == True:\n", 870 | " os.environ[\"skipexistC\"] = \"--ignore-existing\"\n", 871 | " else:\n", 872 | " os.environ[\"skipexistC\"] = \"\"\n", 873 | "\n", 874 | " if Do_not_cross_filesystem_boundaries == True:\n", 875 | " os.environ[\"nocrossfilesystemC\"] = \"--one-file-system\"\n", 876 | " else:\n", 877 | " os.environ[\"nocrossfilesystemC\"] = \"\"\n", 878 | "\n", 879 | " if Do_not_update_modtime_if_files_are_identical == True:\n", 880 | " os.environ[\"noupdatemodtimeC\"] = \"--no-update-modtime\"\n", 881 | " else:\n", 882 | " os.environ[\"noupdatemodtimeC\"] = \"\"\n", 883 | "\n", 884 | " if Large_amount_of_files_optimization == True:\n", 885 | " os.environ[\"filesoptimizeC\"] = \"--fast-list\"\n", 886 | " else:\n", 887 | " os.environ[\"filesoptimizeC\"] = \"\"\n", 888 | "\n", 889 | " if Google_Drive_optimization == True:\n", 890 | " os.environ[\"driveoptimizeC\"] = \"--drive-chunk-size 32M --drive-acknowledge-abuse --drive-keep-revision-forever --disable copy\"\n", 891 | " else:\n", 892 | " os.environ[\"driveoptimizeC\"] = \"\"\n", 893 | "\n", 894 | " if Dry_Run == True:\n", 895 | " os.environ[\"dryrunC\"] = \"-n\"\n", 896 | " else:\n", 897 | " os.environ[\"dryrunC\"] = \"\"\n", 898 | "\n", 899 | " if Output_Log_File != \"OFF\":\n", 900 | " os.environ[\"statsC\"] = \"--log-file=/root/.rclone_log/rclone_log.txt\"\n", 901 | " else:\n", 902 | " if Simple_Ouput == True:\n", 903 | " os.environ[\"statsC\"] = \"-v --stats-one-line --stats=5s\"\n", 904 | " else:\n", 905 | " os.environ[\"statsC\"] = \"-v --stats=5s\"\n", 906 | "\n", 907 | " if Output_Log_File == \"INFO\":\n", 908 | " os.environ[\"loglevelC\"] = \"--log-level INFO\"\n", 909 | " elif Output_Log_File == \"ERROR\":\n", 910 | " os.environ[\"loglevelC\"] = \"--log-level ERROR\"\n", 911 | " elif Output_Log_File == \"DEBUG\":\n", 912 | " os.environ[\"loglevelC\"] = \"--log-level DEBUG\"\n", 913 | " else:\n", 914 | " os.environ[\"loglevelC\"] = \"\"\n", 915 | "\n", 916 | " os.environ[\"extraC\"] = Extra_Arguments\n", 917 | "\n", 918 | " if Sync_Mode == \"Delete during transfer\":\n", 919 | " os.environ[\"syncmodeC\"] = \"--delete-during\"\n", 920 | " elif Sync_Mode == \"Delete before transfering\":\n", 921 | " os.environ[\"syncmodeC\"] = \"--delete-before\"\n", 922 | " elif Sync_Mode == \"Delete after transfering\":\n", 923 | " os.environ[\"syncmodeC\"] = \"--delete-after\"\n", 924 | "\n", 925 | " if Track_Renames == True:\n", 926 | " os.environ[\"trackrenamesC\"] = \"--track-renames\"\n", 927 | " else:\n", 928 | " os.environ[\"trackrenamesC\"] = \"\"\n", 929 | "\n", 930 | " if Deduplicate_Mode == \"Interactive\":\n", 931 | " os.environ[\"deduplicateC\"] = \"interactive\"\n", 932 | " elif Deduplicate_Mode == \"Skip\":\n", 933 | " os.environ[\"deduplicateC\"] = \"skip\"\n", 934 | " elif Deduplicate_Mode == \"First\":\n", 935 | " os.environ[\"deduplicateC\"] = \"first\"\n", 936 | " elif Deduplicate_Mode == \"Newest\":\n", 937 | " os.environ[\"deduplicateC\"] = \"newest\"\n", 938 | " elif Deduplicate_Mode == \"Oldest\":\n", 939 | " os.environ[\"deduplicateC\"] = \"oldest\"\n", 940 | " elif Deduplicate_Mode == \"Largest\":\n", 941 | " os.environ[\"deduplicateC\"] = \"largest\"\n", 942 | " elif Deduplicate_Mode == \"Rename\":\n", 943 | " os.environ[\"deduplicateC\"] = \"rename\"\n", 944 | "\n", 945 | " if Deduplicate_Use_Trash == True:\n", 946 | " os.environ[\"deduplicatetrashC\"] = \"\"\n", 947 | " else:\n", 948 | " os.environ[\"deduplicatetrashC\"] = \"--drive-use-trash=false\"\n", 949 | "\n", 950 | " ### rclone Execution\n", 951 | "\n", 952 | " if Output_Log_File != \"OFF\" and Mode != \"Config\":\n", 953 | " !mkdir -p -m 666 /root/.rclone_log/\n", 954 | " display(HTML(\"

Logging enables, rclone will not output log through the terminal, please wait until finished.


\"))\n", 955 | "\n", 956 | "\n", 957 | " if Mode == \"Config\":\n", 958 | " !rclone --config=/root/.rclone.conf config\n", 959 | " elif Mode == \"Copy\":\n", 960 | " !rclone --config=/root/.rclone.conf copy \"$sourceC\" \"$destinationC\" $transfersC $checkersC $statsC $loglevelC $compareC $skipnewC $skipexistC $nocrossfilesystemC $noupdatemodtimeC $bufferC $filesoptimizeC $driveoptimizeC $dryrunC $extraC\n", 961 | " elif Mode == \"Move\":\n", 962 | " !rclone --config=/root/.rclone.conf move \"$sourceC\" \"$destinationC\" $transfersC $checkersC $statsC $loglevelC --delete-empty-src-dirs $compareC $skipnewC $skipexistC $nocrossfilesystemC $noupdatemodtimeC $bufferC $filesoptimizeC $driveoptimizeC $dryrunC $extraC\n", 963 | " elif Mode == \"Sync\":\n", 964 | " !rclone --config=/root/.rclone.conf sync \"$sourceC\" \"$destinationC\" $transfersC $checkersC $statsC $loglevelC $syncmodeC $trackrenamesC $compareC $skipnewC $skipexistC $nocrossfilesystemC $noupdatemodtimeC $bufferC $filesoptimizeC $driveoptimizeC $dryrunC $extraC\n", 965 | " elif Mode == \"Checker\":\n", 966 | " !rclone --config=/root/.rclone.conf check \"$sourceC\" \"$destinationC\" $checkersC $statsC $loglevelC $compareC $skipnewC $skipexistC $nocrossfilesystemC $noupdatemodtimeC $bufferC $filesoptimizeC $driveoptimizeC $dryrunC $extraC\n", 967 | " elif Mode == \"Deduplicate\":\n", 968 | " !rclone --config=/root/.rclone.conf dedupe \"$sourceC\" $checkersC $statsC $loglevelC --dedupe-mode $deduplicateC $deduplicatetrashC $compareC $skipnewC $skipexistC $nocrossfilesystemC $noupdatemodtimeC $bufferC $filesoptimizeC $driveoptimizeC $dryrunC $extraC\n", 969 | " elif Mode == \"Remove Empty Directories\":\n", 970 | " !rclone --config=/root/.rclone.conf rmdirs \"$sourceC\" $statsC $loglevelC $dryrunC $extraC\n", 971 | " elif Mode == \"Empty Trash\":\n", 972 | " !rclone --config=/root/.rclone.conf cleanup \"$sourceC\" $statsC $loglevelC $dryrunC $extraC\n", 973 | " elif Mode == \"qBittorrent\":\n", 974 | " !chmod -R 666 /content/qBittorrent/\n", 975 | " !rclone --config=/root/.rclone.conf move \"/content/qBittorrent/\" \"$destinationC\" $transfersC $checkersC $statsC $loglevelC --delete-empty-src-dirs --exclude **/.unwanted/ $compareC $skipnewC $skipexistC $nocrossfilesystemC $noupdatemodtimeC $bufferC $filesoptimizeC $driveoptimizeC $dryrunC $extraC\n", 976 | "\n", 977 | " ### Log Output\n", 978 | "\n", 979 | " if Output_Log_File != \"OFF\" and Mode != \"Config\":\n", 980 | " ### Rename log file and output settings.\n", 981 | " !mv /root/.rclone_log/rclone_log.txt /root/.rclone_log/rclone_log_$(date +%Y-%m-%d_%H.%M.%S).txt\n", 982 | " with open(\"/root/.rclone_log/\" + Mode + \"_settings.txt\", \"w\") as f:\n", 983 | " f.write(\"Mode: \" + Mode + \\\n", 984 | " \"\\nCompare: \" + Compare + \\\n", 985 | " \"\\nSource: \\\"\" + Source + \\\n", 986 | " \"\\\"\\nDestination: \\\"\" + Destination + \\\n", 987 | " \"\\\"\\nTransfers: \" + str(Transfers) + \\\n", 988 | " \"\\nCheckers: \" + str(Checkers) + \\\n", 989 | " \"\\nSkip files that are newer on the destination: \" + str(Skip_files_that_are_newer_on_the_destination) + \\\n", 990 | " \"\\nSkip all files that exist: \" + str(Skip_all_files_that_exist) + \\\n", 991 | " \"\\nDo not cross filesystem boundaries: \" + str(Do_not_cross_filesystem_boundaries) + \\\n", 992 | " \"\\nDo not update modtime if files are identical: \" + str(Do_not_update_modtime_if_files_are_identical) + \\\n", 993 | " \"\\nDry-Run: \" + str(Dry_Run) + \\\n", 994 | " \"\\nOutput Log Level: \" + Output_Log_File + \\\n", 995 | " \"\\nExtra Arguments: \\\"\" + Extra_Arguments + \\\n", 996 | " \"\\\"\\nSync Moden: \" + Sync_Mode + \\\n", 997 | " \"\\nTrack Renames: \" + str(Track_Renames) + \\\n", 998 | " \"\\nDeduplicate Mode: \" + Deduplicate_Mode + \\\n", 999 | " \"\\nDeduplicate Use Trash: \" + str(Deduplicate_Use_Trash))\n", 1000 | " ### Compressing log file.\n", 1001 | " !rm -f /root/rclone_log.zip\n", 1002 | " !zip -r -q -j -9 /root/rclone_log.zip /root/.rclone_log/\n", 1003 | " !rm -rf /root/.rclone_log/\n", 1004 | " !mkdir -p -m 666 /root/.rclone_log/\n", 1005 | " ### Send Log\n", 1006 | " if os.path.isfile(\"/root/rclone_log.zip\") == True:\n", 1007 | " try:\n", 1008 | " files.download(\"/root/rclone_log.zip\")\n", 1009 | " !rm -f /root/rclone_log.zip\n", 1010 | " display(HTML(\"

Sending log to your browser...


\"))\n", 1011 | " except:\n", 1012 | " !mv /root/rclone_log.zip /content/rclone_log_$(date +%Y-%m-%d_%H.%M.%S).zip\n", 1013 | " display(HTML(\"

You can use file explorer to download the log file.



\"))\n", 1014 | " else:\n", 1015 | " clear_output(wait=True)\n", 1016 | " display(HTML(\"

There is no log file.


\"))\n", 1017 | "\n", 1018 | " ### Operation has been successfully completed.\n", 1019 | " if os.path.isfile(\"/usr/bin/rclone\") == False:\n", 1020 | " clear_output(wait=True)\n", 1021 | " display(HTML(\"

❌ Please run the installation cell above first.


\"))\n", 1022 | " elif Mode != \"Config\":\n", 1023 | " display(HTML(\"

✅ Operation has been successfully completed.


\"))\n", 1024 | "else:\n", 1025 | " !echo \"doing nothing because the mode is set to off\"" 1026 | ], 1027 | "execution_count": 0, 1028 | "outputs": [] 1029 | }, 1030 | { 1031 | "cell_type": "markdown", 1032 | "metadata": { 1033 | "colab_type": "text", 1034 | "id": "5-5ksQslqmYy" 1035 | }, 1036 | "source": [ 1037 | "# \"Utility\"/" 1038 | ] 1039 | }, 1040 | { 1041 | "cell_type": "code", 1042 | "metadata": { 1043 | "id": "_y3BOjJ1BcDE", 1044 | "colab_type": "code", 1045 | "cellView": "form", 1046 | "colab": {} 1047 | }, 1048 | "source": [ 1049 | "# ============================= FORM ============================= #\n", 1050 | "#@markdown

Ubuntu Virtual Machine Updater

\n", 1051 | "# ================================================================ #\n", 1052 | "\n", 1053 | "from IPython.display import HTML, clear_output\n", 1054 | "\n", 1055 | "!apt update -qq -y\n", 1056 | "!apt upgrade -qq -y\n", 1057 | "!npm i -g npm\n", 1058 | "\n", 1059 | "clear_output(wait=True)\n", 1060 | "display(HTML(\"

An update has been successfully completed.


\"))" 1061 | ], 1062 | "execution_count": 0, 1063 | "outputs": [] 1064 | }, 1065 | { 1066 | "cell_type": "code", 1067 | "metadata": { 1068 | "colab_type": "code", 1069 | "cellView": "form", 1070 | "id": "O-jIeNjdbkOQ", 1071 | "colab": {} 1072 | }, 1073 | "source": [ 1074 | "# ============================= FORM ============================= #\n", 1075 | "#@markdown

Check VM's Status

\n", 1076 | "Loop_Check = False #@param {type:\"boolean\"}\n", 1077 | "Loop_Interval = 5 #@param {type:\"slider\", min:1, max:15, step:1}\n", 1078 | "# ================================================================ #\n", 1079 | "\n", 1080 | "import time\n", 1081 | "from IPython.display import clear_output\n", 1082 | "Loop = True\n", 1083 | "\n", 1084 | "try:\n", 1085 | " while Loop == True:\n", 1086 | " clear_output(wait=True)\n", 1087 | " !top -bcn1 -w512\n", 1088 | " if Loop_Check == False:\n", 1089 | " Loop = False\n", 1090 | " else:\n", 1091 | " time.sleep(Loop_Interval)\n", 1092 | "except:\n", 1093 | " clear_output(wait=True)" 1094 | ], 1095 | "execution_count": 0, 1096 | "outputs": [] 1097 | }, 1098 | { 1099 | "cell_type": "code", 1100 | "metadata": { 1101 | "id": "M4tD3PEMEOH9", 1102 | "colab_type": "code", 1103 | "cellView": "form", 1104 | "colab": {} 1105 | }, 1106 | "source": [ 1107 | "# ============================= FORM ============================= #\n", 1108 | "#@markdown

Get VM's Specification

\n", 1109 | "Output_Format = \"TEXT\" #@param [\"TEXT\", \"HTML\", \"XML\", \"JSON\"]\n", 1110 | "Short_Output = False #@param {type:\"boolean\"}\n", 1111 | "# ================================================================ #\n", 1112 | "\n", 1113 | "import os\n", 1114 | "from google.colab import files\n", 1115 | "from IPython.display import HTML, clear_output\n", 1116 | "\n", 1117 | "try:\n", 1118 | " Output_Format_Ext\n", 1119 | "except NameError:\n", 1120 | " !apt install lshw -qq -y\n", 1121 | " clear_output(wait=True)\n", 1122 | "\n", 1123 | "if Short_Output == True:\n", 1124 | " os.environ[\"outputformatC\"] = \"txt\"\n", 1125 | " os.environ[\"outputformat2C\"] = \"-short\"\n", 1126 | " Output_Format_Ext = \"txt\"\n", 1127 | "elif Output_Format == \"TEXT\":\n", 1128 | " os.environ[\"outputformatC\"] = \"txt\"\n", 1129 | " os.environ[\"outputformat2C\"] = \"\"\n", 1130 | " Output_Format_Ext = \"txt\"\n", 1131 | "else:\n", 1132 | " os.environ[\"outputformatC\"] = Output_Format.lower()\n", 1133 | " os.environ[\"outputformat2C\"] = \"-\"+Output_Format.lower()\n", 1134 | " Output_Format_Ext = Output_Format.lower()\n", 1135 | " \n", 1136 | "!lshw $outputformat2C > Specification.$outputformatC\n", 1137 | "files.download(\"/content/Specification.\" + Output_Format_Ext)\n", 1138 | "!rm -f /content/Specification.$outputformatC\n", 1139 | "display(HTML(\"

Sending log to your browser...


\"))" 1140 | ], 1141 | "execution_count": 0, 1142 | "outputs": [] 1143 | }, 1144 | { 1145 | "cell_type": "code", 1146 | "metadata": { 1147 | "colab_type": "code", 1148 | "cellView": "form", 1149 | "id": "sOKCFBtIkq8a", 1150 | "colab": {} 1151 | }, 1152 | "source": [ 1153 | "# ============================= FORM ============================= #\n", 1154 | "#@markdown
\"netdata\"/
\n", 1155 | "#@markdown

Real-time Server Monitoring


\n", 1156 | "# ============================= FORM ============================= #\n", 1157 | "\n", 1158 | "TornOnNetdata = True #@param {type:\"boolean\"}\n", 1159 | "\n", 1160 | "# ============================= FORM ============================= #\n", 1161 | "# ================================================================ #\n", 1162 | "\n", 1163 | "import os, psutil, IPython, uuid, time\n", 1164 | "import ipywidgets as widgets\n", 1165 | "\n", 1166 | "from IPython.display import HTML, clear_output\n", 1167 | "from google.colab import output\n", 1168 | "import sys\n", 1169 | "\n", 1170 | "\n", 1171 | "\n", 1172 | "\n", 1173 | "SuccessRun = widgets.Button(\n", 1174 | " description='✔ Successfully',\n", 1175 | " disabled=True,\n", 1176 | " button_style='success'\n", 1177 | ")\n", 1178 | "\n", 1179 | "UnsuccessfullyRun = widgets.Button(\n", 1180 | " description='✘ Unsuccessfully',\n", 1181 | " disabled=True,\n", 1182 | " button_style='danger'\n", 1183 | ")\n", 1184 | "\n", 1185 | "class MakeButton(object):\n", 1186 | " def __init__(self, title, callback):\n", 1187 | " self._title = title\n", 1188 | " self._callback = callback\n", 1189 | " def _repr_html_(self):\n", 1190 | " callback_id = 'button-' + str(uuid.uuid4())\n", 1191 | " output.register_callback(callback_id, self._callback)\n", 1192 | " template = \"\"\"\n", 1193 | " \"\"\"\n", 1199 | " html = template.format(title=self._title, callback_id=callback_id)\n", 1200 | " return html\n", 1201 | "\n", 1202 | "def RandomGenerator():\n", 1203 | " return time.strftime(\"%S\") + str(time.time()).split(\".\")[-1]\n", 1204 | "\n", 1205 | "def CheckProcess(process, command):\n", 1206 | " for pid in psutil.pids():\n", 1207 | " try:\n", 1208 | " p = psutil.Process(pid)\n", 1209 | " if process in p.name():\n", 1210 | " for arg in p.cmdline():\n", 1211 | " if command in str(arg): \n", 1212 | " return True\n", 1213 | " else:\n", 1214 | " pass\n", 1215 | " else:\n", 1216 | " pass\n", 1217 | " except:\n", 1218 | " continue\n", 1219 | "\n", 1220 | "def AutoSSH(name,port):\n", 1221 | " get_ipython().system_raw(\"autossh -l \" + name + \" -M 0 -fNT -o 'StrictHostKeyChecking=no' -o 'ServerAliveInterval 300' -o 'ServerAliveCountMax 30' -R 80:localhost:\" + port + \" ssh.localhost.run &\")\n", 1222 | " get_ipython().system_raw(\"autossh -M 0 -fNT -o 'StrictHostKeyChecking=no' -o 'ServerAliveInterval 300' -o 'ServerAliveCountMax 30' -R \" + name + \":80:localhost:\" + port + \" serveo.net &\")\n", 1223 | "\n", 1224 | "def Start_ServerMT():\n", 1225 | " if CheckProcess(\"netdata\", \"\") != True:\n", 1226 | " !/usr/sbin/netdata\n", 1227 | " if CheckProcess(\"autossh\", \"mt\" + Random_NumberMT) != True:\n", 1228 | " AutoSSH(\"mt\" + Random_NumberMT, \"19999\")\n", 1229 | "\n", 1230 | " \n", 1231 | " \n", 1232 | "if TornOnNetdata:\n", 1233 | "\n", 1234 | "\n", 1235 | " try:\n", 1236 | " try:\n", 1237 | " Random_NumberMT\n", 1238 | " except NameError:\n", 1239 | " Random_NumberMT = RandomGenerator()\n", 1240 | " if os.path.isfile(\"/usr/bin/autossh\") == False:\n", 1241 | " !apt update -qq -y\n", 1242 | " !apt install autossh -qq -y\n", 1243 | " clear_output(wait=True)\n", 1244 | " if os.path.isfile(\"/usr/sbin/netdata\") == False:\n", 1245 | " !bash <(curl -Ss https://my-netdata.io/kickstart.sh) --dont-wait --dont-start-it\n", 1246 | " clear_output(wait=True)\n", 1247 | " Start_ServerMT()\n", 1248 | " display(SuccessRun)\n", 1249 | " display(MakeButton(\"Recheck\", Start_ServerMT))\n", 1250 | " display(HTML(\"

Website 1
\" \\\n", 1251 | " \"Website 2

\"))\n", 1252 | " except:\n", 1253 | " clear_output(wait=True)\n", 1254 | " display(UnsuccessfullyRun)\n", 1255 | "else:\n", 1256 | " !echo \"netdata not turned on\"" 1257 | ], 1258 | "execution_count": 0, 1259 | "outputs": [] 1260 | } 1261 | ] 1262 | } 1263 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google-Colab-QBittorrent 2 | 3 | This is a Google Colab notebook with QBittorrent and Rclone. 4 | 5 | I took https://github.com/MinorMole/RcloneLab as a base and edited it to my likings. 6 | 7 | 8 | Features: 9 | 1. QBittorrent as web interface 10 | 2. Ability to mount Google Drive with either rClone or the colab drive plugin 11 | 3. Ability to edit the QBittorrent config trough the notebook to make it persistant 12 | 4. Ability to change the qbittorrent webUI subdomain trough the notebook 13 | 4. 14 search engines Pre-installed on Qbittorrent (On the webUI Click on view --> Search) 14 | 5. File browser 15 | 6. Netdata as system usage monitor 16 | 7. Now also includes JDownloader! 17 | 18 | 19 | 20 |
Usage: 21 | 1. Click on the "Open in Colab" button and press ctrl+f9 22 | 23 | Open In Colab 24 | 25 | 26 | 27 |
28 | Below is a screenshot of the main part of netdata while downloading: 29 |
30 |
31 | 32 |
33 |
34 | 35 | 36 |


37 | For the people not familiar with the webUI, this is what it looks like:
38 | 39 | 40 | --------------------------------------------------------------------------------