├── 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 | " "
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 profile4>\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 | "# "
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 = \"\"\"{title} \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 \"))\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 | "# "
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 = \"\"\"{title} \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 | "# "
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 = \"\"\"{title} \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 | "# "
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 | "# "
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 \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 = \"\"\"{title} \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(\"\"))\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 |
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 |
--------------------------------------------------------------------------------