From ee3b15c29baec5c79521d4a63b20c6687b370ae5 Mon Sep 17 00:00:00 2001
From: a <a@trwnh.com>
Date: Wed, 23 Oct 2024 04:35:02 +0000
Subject: [PATCH] Add ytlogin command for OAuth against YouTube

---
 .gitignore         |  1 +
 README.md          |  7 ++++-
 cache/.gitkeep     |  0
 cogs/music.py      | 64 +++++++++++++++++++++++++++++++++++++++++++---
 config/.netrc      |  1 +
 docker-compose.yml |  4 +--
 6 files changed, 70 insertions(+), 7 deletions(-)
 create mode 100644 cache/.gitkeep
 create mode 100644 config/.netrc

diff --git a/.gitignore b/.gitignore
index 7995a02..8c3ccea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 .env
 .venv
 .cache/*
+cache/*
 .logs/*
 logs/*
 sounds/originals/*
diff --git a/README.md b/README.md
index a53b0bc..a5c4499 100644
--- a/README.md
+++ b/README.md
@@ -49,4 +49,9 @@ python app.py
 ```sh
 docker compose build
 docker compose up -d
-```
\ No newline at end of file
+```
+
+## Log into YouTube
+
+- Follow the instructions at https://github.com/yt-dlp/yt-dlp/wiki/Extractors#exporting-youtube-cookies
+- Put your extracted cookies into config/cookies.txt
\ No newline at end of file
diff --git a/cache/.gitkeep b/cache/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/cogs/music.py b/cogs/music.py
index f117e86..fa1430e 100644
--- a/cogs/music.py
+++ b/cogs/music.py
@@ -593,6 +593,65 @@ class Music(Cog):
 				logger.info(f"Message sent: Playing {len(tracks)} tracks.")
 		await self.play_next(ctx)
 
+	@command()
+	async def ytlogin(self, ctx: Context):
+		"""Print token and instructions for authorizing with YouTube"""
+		logger.info(f".ytlogin")
+		process = subprocess.Popen(
+			[
+				"yt-dlp",
+				"--netrc",
+				"--skip-download",  # we don't care to actually download the video, we just wanna authorize
+				"https://www.youtube.com/watch?v=dQw4w9WgXcQ"  # this can be anything, it just needs a youtube URL
+			],
+			stdout=subprocess.PIPE,
+			encoding="utf-8",
+		)
+		while True:
+			line = process.stdout.readline()
+			if not line:
+				logger.debug("Subprocess ran out of lines in stdout")
+				break
+			if "google.com" in line:
+				msg = await ctx.send(line)
+				if msg:
+					logger.info(f"Message sent: {line}")
+				break
+		timeout = time() + 30  # TODO: make this variable instead of hardcoded?
+		while (process.poll() is None) and (time() < timeout):
+			pass  # this loop will break when the process exits with a return code, or when the timeout is exceeded
+		if process.poll() is None:
+			logger.debug("Process still hasn't exited yet, so we need to kill it and let the user know that the authorization process failed")
+			process.kill()
+			text = "Authorization process timed out."
+			msg = await ctx.send(text)
+			if msg:
+				logger.info(f"Message sent: {text}")
+			return
+		else:
+			logger.debug("Process has exited with a return code, so check if the return code indicates no errors.")
+			if process.returncode == 0:
+				text = "The process has finished with no errors, so you're probably logged in now."
+				msg = await ctx.send(text)
+				if msg:
+					logger.info(f"Message sent: {text}")
+				process.kill()  # just in case
+				return
+			else:
+				process.kill()  # just in case
+				out, err = process.communicate()
+				if err:
+					logger.error(f"An error occurred during the authorization process: {err}")
+					msg = await ctx.send(
+						f"An error occurred during the authorization process:\n",
+						f"```\n",
+						f"{err}",
+						f"```",
+					)
+					if msg:
+						logger.info(f"Message sent: An error occurred during the authorization process")
+				return
+
 	@command(aliases=['p', 'listen'])
 	async def play(self, ctx: Context, *, query: str = ""):
 		"""Add track(s) to queue"""
@@ -866,9 +925,6 @@ ytdl_format_options = {
 	"extract_flat": True, # massive speedup for fetching metadata, at the cost of no upload date
 	#"cookiefile": "cookies.txt",
 	"cachedir": "cache",
-	"username": "oauth",
-	"password": "",
+	"usenetrc": True
 }
-username = getenv("YOUTUBE_USERNAME")
-password = getenv("YOUTUBE_PASSWORD")
 ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
\ No newline at end of file
diff --git a/config/.netrc b/config/.netrc
new file mode 100644
index 0000000..7ee24e6
--- /dev/null
+++ b/config/.netrc
@@ -0,0 +1 @@
+machine youtube login oauth password ""
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 91a4e87..8feb4ae 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -13,6 +13,6 @@ services:
       - ./cogs:/umi/cogs
       - ./sounds:/umi/sounds
       - ./logs:/umi/.logs
-      - ./cookies.txt:/umi/cookies.txt
-      - ./.netrc:/root/.netrc
+      - ./config/cookies.txt:/umi/cookies.txt
+      - ./config/.netrc:/root/.netrc
       - ./cache:/umi/cache
\ No newline at end of file