123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- # This is a youtube-dl youtube song downloader. It uses both youtube-dl
- # for actual download and the invidious API for fetching info about the
- # song. GPLv3+
- import os
- from subprocess import *
- import json
- import random
- import urllib.request
- import urllib.parse
- from yt_dlp import YoutubeDL
- dfolder = os.path.expanduser("~/.matrix_music")
- youtubes = ["music.youtube.com", # That has to be in front, for strip function to work properly.
- "youtube.com", # Since in there is "youtube.com" in "music.youtube.com"
- "youtu.be"]
- invidious = ["invidious.namazso.eu",
- "yewtu.be",
- "invidious.snopyta.org",
- "invidious.kavin.rocks",
- "inv.riverside.rocks",
- "yt.artemislena.eu",
- "invidious.flokinet.to",
- "invidious.esmailelbob.xyz",
- "inv.bp.projectsegfau.lt",
- "y.com.sb",
- "invidious.tiekoetter.com",
- "invidious.nerdvpn.de",
- "invidious.slipfox.xyz",
- "watch.thekitty.zone",
- "inv.odyssey346.dev",
- "invidious.baczek.me",
- "invidious.weblibre.org",
- "invidious.privacydev.net",
- "yt.funami.tech"
- ]
- other_links = ["c7hqkpkpemu6e7emz5b4vyz7idjgdvgaaa3dyimmeojqbgpea3xqjoid.onion",
- "w6ijuptxiku4xpnnaetxvnkc5vqcdu7mgns2u77qefoixi63vbvnpnqd.onion",
- "kbjggqkzv65ivcqj6bumvp337z6264huv5kpkwuv6gu5yjiskvan7fad.onion",
- "grwp24hodrefzvjjuccrkw3mjq4tzhaaq32amf33dzpmuxe7ilepcmad.onion",
- "osbivz6guyeahrwp2lnwyjk2xos342h4ocsxyqrlaopqjuhwn2djiiyd.onion",
- "u2cvlit75owumwpy4dj2hsmvkq7nvrclkpht7xgyye2pyoxhpmclkrad.onion",
- "euxxcnhsynwmfidvhjf6uzptsmh4dipkmgdmcmxxuo7tunp3ad2jrwyd.onion",
- "invidious.esmail5pdn24shtvieloeedh7ehz3nrwcdivnfhfcedl7gf4kwddhkqd.onion",
- "inv.vernccvbvyi5qhfzyqengccj7lkove6bjot2xhh5kajhwvidqafczrad.onion",
- "am74vkcrjp2d5v36lcdqgsj2m6x36tbrkhsruoegwfcizzabnfgf5zyd.onion",
- "ng27owmagn5amdm7l5s3rsqxwscl5ynppnis5dqcasogkyxcfqn7psid.onion",
- "iv.odysfvr23q5wgt7i456o5t3trw2cw5dgn56vbjfbq2m7xsc5vqbqpcyd.onion",
- "invidious.g4c3eya4clenolymqbpgwz3q3tawoxw56yhzk4vugqrl6dtu3ejvhjid.onion",
- "verni6dr4qxjgjumnvesxerh5rvhv6oy5ddeibaqy5d7tgbiiyfa.b32.i2p",
- "vid.priv.au",
- "invidious.lidarshield.cloud",
- "invidious.silur.me",
- "iv.melmac.space",
- "iv.ggtyler.dev",
- "invidious.epicsite.xyz",
- "invidious.sethforprivacy.com",
- "yt.oelrichsgarcia.de",
- "invidious.drivet.xyz",
- "invidious.dhusch.de",
- "inv.vern.cc",
- "vid.puffyan.us"
- "piped.kavin.rocks",
- "viewtube.io"]
-
- all_instances = []
- for t in [youtubes, invidious, other_links]:
- for i in t:
- all_instances.append(i)
- class Logger(object):
- def debug(self, msg):
- pass
- def warning(self, msg):
- pass
- def error(self, msg):
- pass
- ydl_opts = {'logger': Logger()}
- def difference(x,y):
- if x > y:
- return x - y
- else:
- return y - x
- def fetch_playlist(playlistId):
- try:
- try:
- url = "https://invidious.namazso.eu/api/v1/playlists/"+playlistId
- req = urllib.request.Request(url, data=None)
- f = urllib.request.urlopen(req)
- data = json.loads(f.read().decode('utf-8'))
- except:
- with YoutubeDL(ydl_opts) as ydl:
- dump = ydl.extract_info(url=playlistId, download=False)
- with open('dump.json', 'w') as fp:
- json.dump(dump, fp, indent=4)
- # Converting youtube_dl data into Invidious type object
- data = {"videos":[]}
- for video in dump.get("entries", []):
- data["videos"].append({"videoId":video.get("id", "")})
-
- return data
-
- except Exception as e:
- print(" [ \033[31mFAILED\033[00m ] youtube_dl |", e)
- return str(e)
-
-
- def fetch_info(videoId):
- try:
- with YoutubeDL(ydl_opts) as ydl:
- dump = ydl.extract_info(url=videoId, download=False)
- with open('dump.json', 'w') as fp:
- json.dump(dump, fp, indent=4)
- # Converting youtube_dl data into Invidious type object
- data = {"lengthSeconds":dump.get("duration", 999999999999999),
- "title":dump.get("title", ""),
- "author":dump.get("channel", "")}
-
- return data
-
- except Exception as e:
- print(" [ \033[31mFAILED\033[00m ] youtube_dl |", e)
-
- for instance in invidious:
- try:
- url = "https://"+instance+"/api/v1/videos/"+videoId
- req = urllib.request.Request(url, data=None)
- f = urllib.request.urlopen(req)
- data = json.loads(f.read().decode('utf-8'))
- return data
- except Exception as e:
- print(" [ \033[31mFAILED\033[00m ] "+instance+" |", e)
-
-
- def search(query):
- # This function will search on youtube a query.
- for instance in invidious:
- try:
- url = "https://"+instance+"/api/v1/search/?q="+urllib.parse.quote_plus(query)
- req = urllib.request.Request(url, data=None)
- f = urllib.request.urlopen(req)
- data = json.loads(f.read().decode('utf-8'))
- return data
- except Exception as e:
- print(" [ \033[31mFAILED\033[00m ] "+instance+" |", e)
-
- def try_download(videoId, filename):
-
- # This fucntion will try download the song via youtube-dl
- #print("Trying to download", videoId)
-
- # Making sure that the download folder exists
- try:
- os.mkdir(dfolder)
- except:
- pass
-
- try:
- # We are using subbrocess.check_output here to catch errors.
- check_output(["yt-dlp",
- "https://youtube.com/watch?v="+videoId,
- "-x",
- "--output",
- dfolder+"/"+str(filename)+".%(ext)s"])
- for i in os.listdir(dfolder):
- if i.startswith(filename):
- return dfolder+"/"+i
- return dfolder+"/"+str(filename)
-
- except Exception as e:
- print(" [ \033[31mFAILED\033[00m ] youtube_dl |", e)
- return False
- def insure_song(videoId, filename):
- # This function will insure a song is downloaded.
- songInfo = fetch_info(videoId)
- #print("Song", songInfo["title"], "by", songInfo["author"], "direct attempt.")
-
- # First we will try downloading the song as is.
- d = try_download(videoId, filename)
- if d:
- #print("Worked!")
- return d
- else:
- #print("Failed! Trying similar...")
-
- # If it failed we are going to try downloading a similar song.
- titleblacklist = ["cover", "react", "review", "remix", "acoustic", "teaser", "live"]
- query = songInfo["title"]
- if " - Topic" in songInfo["author"]:
- query = songInfo["author"].replace(" - Topic", "")+" "+query
-
- others = search(query)
- for song in others:
- # Chekcing that it's a video
- if song["type"] != "video":
- continue
-
- # Checking if the song is a cover or a remix
-
- cont = False
- for i in titleblacklist:
- if i.lower() in song["title"].lower():
- cont = i
- break
- if cont:
- #print("Skipped:", song["title"], "because", cont )
- continue
- # Chekcing the length of the song to be roughly the original
- if difference(song["lengthSeconds"], songInfo["lengthSeconds"]) > 60:
- continue
- #print("Trying to download", song["title"])
-
- d = try_download(song["videoId"], filename)
- if d:
- return d
- return False
- def strip(url):
- """Function that strips youtube links. And leaves only the videoId."""
-
- videoId = url
- # Maybe it's a playlist
- if "list=" in videoId and "/watch?v=" not in videoId:
- videoId = videoId[videoId.find("list=")+5:]
-
- videoId = videoId.replace("http://", "").replace("https://", "").replace("www.", "")
- for i in all_instances:
- videoId = videoId.replace(i, "")
- videoId = videoId.replace("/watch?v=", "")
- if "&" in videoId:
- videoId = videoId[:videoId.find("&")]
- videoId = videoId.replace("/", "")
-
- return videoId
|