feat(stremio): create updated stremio gdrive guide with OAuth tool to get credentials (#87)

* feat: add stremio gdrive guide with oauth tool

* revert: put back es locale

* feat: add some logging

* feat: use web application instead

* feat: update images to reflect change to web application

* fix: use correct heading id

* trying to fix toasts

* idk
This commit is contained in:
Viren070
2024-12-07 02:20:51 +00:00
committed by GitHub
parent f2040fd67c
commit 3f3f5a8bf2
30 changed files with 540 additions and 14 deletions
+195 -4
View File
@@ -1,14 +1,19 @@
---
title: Stremio Gdrive
title: Stremio GDrive
description: Stremio addon for fetching videos from your google drive.
keywords: [stremio, addons, http, google drive, guide, setup, configuration, install, link]
tags: [stremio-addon, stremio-addon:http-streams]
toc_max_heading_level: 2
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Details from '@theme/Details';
import {CallBackUrl} from '@site/src/components/GoogleOAuth/Callback';
```mdx-code-block
import StremioAddonButtons from '@site/src/components/StremioAddonButtons';
<StremioAddonButtons id="stremio-gdrive" source="https://github.com/ShuvamJaswal/Gdrive-Stremio-Update" />
<StremioAddonButtons id="stremio-gdrive" />
```
<h3>Description</h3>
@@ -18,8 +23,194 @@ Stremio-gdrive is a self-hosted Stremio addon that allows you to view files from
For it to work, you will need to join some team drives. You can find some [here](https://t.me/s/teamdrives).
<h3>Configuration</h3>
I will go through the steps to set up this addon through a Cloudflare Worker.
As mentioned earlier, this addon needs to be self-hosted.
:::tip
**This guide should be followed on its [dedicated page](/stremio/addons/stremio-gdrive), not from the setup page.**
:::
<h4>Setting up our Google App</h4>
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
If this is your first time using Google Cloud, you will be prompted to agree to the terms of service:
![Google Cloud Console](/img/stremio/gdrive/google_cloud_onboarding.png)
2. Create a new project and select it:
<Details summary="How?">
1. Click on `Select a project` in the top left:
![Create a new project](/img/stremio/gdrive/google_cloud_create_project_1.png)
2. Click on `New Project`:
![Create a new project](/img/stremio/gdrive/google_cloud_create_project_2.png)
3. Enter a project name and click on `Create`:
![Create a new project](/img/stremio/gdrive/google_cloud_create_project_3.png)
- The project name can be anything you want, e.g., `Stremio-Gdrive`. Leave the `Organization` field blank.
4. Once the project has been created, you will get a notification:
![Create a new project](/img/stremio/gdrive/google_cloud_create_project_4.png)
- Click `Select Project`.
:::note
You may also use the same dropdown from step i to select the project.
:::
</Details>
3. Setup our Google Auth Platform
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
2. In the search bar at the top, search for `Google Auth Platform` and click on the result:
![Google OAuth Platform](/img/stremio/gdrive/google_cloud_oauth_search_results.png)
3. You should be met with a message telling you that `Google Auth Platform not configured yet`, click on `Get Started`:
![Google OAuth Platform](/img/stremio/gdrive/google_auth_platform_0.png)
4. Fill in the form:
![Google OAuth Platform](/img/stremio/gdrive/google_auth_platform_1.png)
1. `App Information`:
- Set `App Name` to `Stremio GDrive`.
- Set `User Support Email` to your email. It should appear in the dropdown.
2. `Audience`:
- Set `User Type` to `External`.
3. `Contact Information`
- Add any email address, you can use the same one you used earlier for `User Support Email`.
4. `Finish`
- Check the box to agree to the `Google API Services: User Data Policy`
5. Once you have filled in the form, click on `Create`
4. Enable the Google Drive API.
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
2. In the search bar at the top, search for `Google Drive API` and click on the result:
![Google Drive API](/img/stremio/gdrive/google_cloud_gdrive_api_search_results.png)
3. Click on `Enable`:
![Google Drive API](/img/stremio/gdrive/google_cloud_gdrive_api.png)
5. Create an OAuth client.
1. Go back to the `Google Auth Platform` page.
2. Click on `Clients` in the sidebar and then click on `+ Create Client`:
![Google OAuth Platform](/img/stremio/gdrive/google_cloud_auth_platform_clients.png)
3. Fill in the form:
![Google OAuth Platform](/img/stremio/gdrive/google_cloud_auth_platform_clients_form.png)
- `Application Type`: Set this to `Web application`.
- `Name`: You can set this to anything such as `Stremio GDrive`.
- `Authorized redirect URIs`: Set this to <CallBackUrl />
4. Click on `Create`.
6. Publish the app.
1. Go back to the `Google Auth Platform` page.
2. Click on `Audience` in the sidebar and then click on `Publish`:
![Google OAuth Platform](/img/stremio/gdrive/google_cloud_auth_platform_publish.png)
<h4>Setting up the Cloudflare Worker</h4>
1. Go to the [Cloudflare Workers](https://workers.cloudflare.com/) page and click `Log In` or `Sign Up` if you don't have an account.
2. Once logged in, you should be taken to the Cloudflare Workers & Pages dashboard. Click on `Create`:
![Cloudflare Workers](/img/stremio/gdrive/cloudflare_workers_pages_overview.png)
3. Once on the create page, make sure you're on the `Workers` tab and click `Create Worker`:
![Cloudflare Workers](/img/stremio/gdrive/cloudflare_create_an_application_workers.png)
4. You'll be asked to give a name to your worker. You can name it anything, this will be the URL you enter into Stremio to access your addon. Click `Deploy` once named.
![Cloudflare Workers](/img/stremio/gdrive/cloudflare_create_a_worker.png)
5. Once its done being deployed, you should be shown a success message. Click the `Edit code` button:
![Cloudflare Workers](/img/stremio/gdrive/cloudflare_create_a_worker_success.png)
6. You should be taken to the Cloudflare Worker editor:
![Cloudflare Workers](/img/stremio/gdrive/cloudflare_worker_editor.png)
7. Now, we will paste the Cloudflare Worker script from the [Stremio GDrive GitHub](https://github.com/Viren070/stremio-gdrive) into the editor.
1. Go to the [worker.js file](https://github.com/Viren070/stremio-gdrive-addon/blob/main/worker.js)
2. Click the Copy icon
3. Go back to the Cloudflare Worker editor and paste the script in the editor.
8. Now, we need to add our Google OAuth credentials to the script.
To do this, we need to go back to the Google Cloud Console and get our `Client ID` and `Client Secret`.
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
2. In the search bar at the top, search for `Google Auth Platform` and click on the result:
![Google OAuth Platform](/img/stremio/gdrive/google_cloud_oauth_search_results.png)
3. Click on `Clients` and click on the download icon for the client you created earlier:
![Google OAuth Platform](/img/stremio/gdrive/google_auth_platform_clients_download.png)
4. A pop-up will appear with your `Client ID` and `Client Secret`. Copy these values into the inputs below at the [OAuth Tool](#oauth-tool).
![Google OAuth Platform](/img/stremio/gdrive/google_auth_platform_clients_download_menu.png)
5. Click the `Get Authorisation Code` button below to get the Authorisation code.
:::tip
You will be redirected to a Google OAuth page where you will need to sign in and allow the app to access your Google Drive.
You may encounter a warning page, click on `Advanced` and then `Go to... (unsafe)`.
:::
6. Once you have allowed the app, you will be redirected back to this page with the Authorisation code filled in.
Click the `Get Credentials` button to get the credentials.
7. You will be shown a success message and a block of code will appear. Copy this code into the Cloudflare Worker script.
8. Once you have added your credentials, your script should look something like this:
![Cloudflare Workers](/img/stremio/gdrive/cloudflare_finished_worker_script.png)
9. Click `Deploy` in the top right to save your changes and deploy the worker.
11. Once deployed, you should see a green success message at the bottom. Click `Visit` next to the deploy button to go to the addon URL.
12. You should be redirected to the /manifest.json. if not, append `/manifest.json` to the URL.
13. Copy the URL and [add it to Stremio](/stremio/faq#how-do-i-install-an-addon-manually).
### OAuth Tool
import GoogleOAuth from '@site/src/components/GoogleOAuth';
<GoogleOAuth />
You can use [this GitHub repository](https://github.com/ShuvamJaswal/Gdrive-Stremio-Update) to set it up.
@@ -0,0 +1,7 @@
import Callback from '@site/src/components/GoogleOAuth/Callback';
<Callback />
# Google Drive Callback
You will automatically be redirected back to the addon guide after you have authenticated with Google.
+38
View File
@@ -0,0 +1,38 @@
import React, {useState} from "react";
import useBaseUrl from "@docusaurus/useBaseUrl";
import { sessionKeys } from ".";
export default function Catcher(): JSX.Element {
const redirectUrl = useBaseUrl('/stremio/addons/stremio-gdrive#oauth-tool');
React.useEffect(() => {
const url = new URL(window.location.href);
const code = url.searchParams.get("code");
if (code) {
sessionStorage.setItem(sessionKeys.authorisationCode, code);
window.location.href = redirectUrl;
}
const error = url.searchParams.get("error");
if (error) {
console.error("Google OAuth Error: ", error);
sessionStorage.setItem(sessionKeys.oauthError, error);
window.location.href = redirectUrl;
}
}, []);
return <></>;
}
export function CallBackUrl(): JSX.Element {
const callbackPath = useBaseUrl('/stremio/addons/stremio-gdrive/callback');
const [redirectUrl, setRedirectUrl] = useState<string>("");
React.useEffect(() => {
// set the redirect url to the current page
setRedirectUrl(window.location.origin + callbackPath);
}, []);
return <>
{redirectUrl}
</>
}
+209
View File
@@ -0,0 +1,209 @@
import React, { useState } from "react";
import CodeBlock from "@theme/CodeBlock";
import { showToast } from "@site/src/components/Toasts";
import styles from "./styles.module.css";
import useBaseUrl from "@docusaurus/useBaseUrl";
const scopes = ['https://www.googleapis.com/auth/drive'];
const oauthUrl = "https://accounts.google.com/o/oauth2/v2/auth";
const tokenUrl = "https://oauth2.googleapis.com/token";
export const sessionKeys = {
clientId: "Viren070Guides_OAuthTool_clientId",
clientSecret: "Viren070Guides_OAuthTool_clientSecret",
authorisationCode: "Viren070Guides_OAuthTool_authorisationCode",
oauthError: "Viren070Guides_OAuthTool_oauthError"
};
const openAuthorisationUrl = (clientId: string, redirectUrl: string) => {
if (clientId.length === 0) {
showToast("Client ID is required", "error");
return;
}
const url = new URL(oauthUrl);
url.searchParams.append("client_id", clientId);
url.searchParams.append("redirect_uri", redirectUrl);
url.searchParams.append("response_type", "code");
url.searchParams.append("scope", scopes.join(" "));
url.searchParams.append("prompt", "consent");
url.searchParams.append("access_type", "offline");
// open the url in current tab
console.log("Opening URL: ", url.toString());
window.open(url.toString(), "_self");
};
export default function CodeGenerator(): JSX.Element {
const [clientId, setClientId] = useState<string>("");
const [clientSecret, setClientSecret] = useState<string>("");
const [authorisationCode, setAuthorisationCode] = useState<string>("");
const [refreshToken, setRefreshToken] = useState<string>("");
const [redirectUrl, setRedirectUrl] = useState<string>("");
const callbackPath = useBaseUrl('/stremio/addons/stremio-gdrive/callback');
React.useEffect(() => {
// set the redirect url to the current page
setRedirectUrl(window.location.origin + callbackPath);
console.log("Redirect URL: ", redirectUrl);
// load the stored values from session storage if they exist
const storedClientId = sessionStorage.getItem(sessionKeys.clientId);
const storedClientSecret = sessionStorage.getItem(sessionKeys.clientSecret);
const storedAuthorisationCode = sessionStorage.getItem(sessionKeys.authorisationCode);
const storedOauthError = sessionStorage.getItem(sessionKeys.oauthError);
if (storedOauthError) {
showToast(`Google OAuth Error: ${storedOauthError}`, "error");
sessionStorage.removeItem(sessionKeys.oauthError);
}
if (storedClientId) {
setClientId(storedClientId);
}
if (storedClientSecret) {
setClientSecret(storedClientSecret);
}
if (storedAuthorisationCode) {
setAuthorisationCode(storedAuthorisationCode);
sessionStorage.removeItem(sessionKeys.authorisationCode);
showToast("Authorisation code obtained successfully. Click `Get Credentials` to complete the process", "success");
}
}, []);
const handleClientIdChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setClientId(value);
sessionStorage.setItem(sessionKeys.clientId, value);
// reset the authorisation code since its no longer valid with different client id
setAuthorisationCode("");
sessionStorage.removeItem(sessionKeys.authorisationCode);
};
const handleClientSecretChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setClientSecret(value);
sessionStorage.setItem(sessionKeys.clientSecret, value);
// reset the authorisation code since its no longer valid with different client secret
setAuthorisationCode("");
sessionStorage.removeItem(sessionKeys.authorisationCode);
};
const getRefreshCode = () => {
if (!clientId || clientId.length === 0) {
showToast("Client ID is required", "error");
return;
}
if (!clientSecret || clientSecret.length === 0) {
showToast("Client Secret is required", "error");
return;
}
if (!authorisationCode || authorisationCode.length === 0) {
showToast("Authorisation Code is required", "error");
return;
}
const data = new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
code: authorisationCode,
grant_type: "authorization_code",
redirect_uri: redirectUrl,
scope: ''
});
console.log("Requesting refresh token with data: ", data.toString());
const response = fetch(tokenUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: data.toString()
});
response.then(handleResponse);
}
const handleResponse = (res: Response) => {
if (res.ok) {
res.json().then((data) => {
console.log(data);
// get the refresh token by making a request to the token url
if (!data.refresh_token) {
showToast("An unexpected error occurred", "error");
return;
}
showToast("Refresh token obtained successfully. Copy the code and paste it into your script.", "success");
// reset the authorisation code as it can no longer be used
setAuthorisationCode("");
sessionStorage.removeItem(sessionKeys.authorisationCode);
setRefreshToken(data.refresh_token);
});
} else {
let text = res.text();
text.then((data) => {
showToast(`Error getting the refresh token:${data}`, "error");
});
// reset any previously obtained refresh token
setRefreshToken("");
}
};
const getCodeBlock = () => {
return (
<CodeBlock language="jsx">
{
`var credentials = {
"client_id": "${clientId}",
"client_secret": "${clientSecret}",
"refresh_token": "${refreshToken}"
};`
}
</CodeBlock>
);
};
return (
<div className={styles.googleAuthFormContainer} >
<div>
</div>
<div>
<label className={styles.googleAuthFormLabel} htmlFor="client-id">Client ID: </label>
<input className={styles.googleAuthFormInput} id="client-id" type="text" placeholder="Client ID" value={clientId} onChange={handleClientIdChange} />
</div>
<div>
<label className={styles.googleAuthFormLabel} htmlFor="client-secret">Client Secret: </label>
<input className={styles.googleAuthFormInput} type="text" placeholder="Client Secret" value={clientSecret} onChange={handleClientSecretChange} />
</div>
<div>
<button className={`${styles.googleAuthFormButton} default-themed-button`} onClick={() => openAuthorisationUrl(clientId, redirectUrl)}>Get Authorisation Code</button>
</div>
<div>
<label className={styles.googleAuthFormLabel} htmlFor="authorisation-code">Authorisation Code: </label>
<input className={styles.googleAuthFormInput} type="text" placeholder="Authorisation Code" value={authorisationCode} onChange={(e) => setAuthorisationCode(e.target.value)} />
</div>
<div>
<button className={`${styles.googleAuthFormButton} default-themed-button`} onClick={getRefreshCode}>Get Credentials</button>
</div>
<div>
{refreshToken.length > 0 && getCodeBlock()}
</div>
<div>
<p style={{"textAlign": "center", "fontWeight": "inherit", "marginTop": "20px"}}>
View the <a href="https://github.com/Viren070/guides/blob/main/src/components/GoogleOAuth/index.tsx" target="_blank" rel="noreferrer">source code</a> for this tool
</p>
</div>
</ div>
);
}
@@ -0,0 +1,50 @@
.googleAuthFormContainer {
max-width: 600px;
margin: 0 auto;
outline: 1px solid var(--ifm-color-primary);
padding: 15px;
border-radius: var(--ifm-global-radius);
}
.googleAuthFormContainer > * {
margin-top: 10px;
}
.googleAuthFormLabel {
display: block;
margin-bottom: 5px;
font-weight: bold;
margin-left: 10px;
}
.googleAuthFormInput {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid var(--ifm-color-primary);
box-sizing: border-box;
}
.googleAuthFormInput:focus {
outline: none;
border-color: var(--ifm-color-primary-darkest);
box-shadow: 0 0 5px var(--ifm-color-primary-darkest);
}
.googleAuthFormButton {
width: 100%;
size: 16px;
font-size: 16px;
font-weight: bold;
}
.googleAuthFormButton:hover {
transform: scale(1.025);
box-shadow: 0 0 5px var(--ifm-color-primary-darkest);
border-color: 0 0 5px var(--ifm-color-primary-darkest);
outline: none;
}
+3 -4
View File
@@ -6,11 +6,11 @@ export const showToast = (message: string, type: 'info' | 'error' | 'success', t
toast(message, {
type: type,
toastId: toastId,
className: styles.customToast,
className: styles.wideToast,
});
};
export function MyToastContainer(): JSX.Element {
export function WideToastContainer(): JSX.Element {
return (
<ToastContainer
stacked
@@ -20,10 +20,9 @@ export function MyToastContainer(): JSX.Element {
draggablePercent={60}
draggableDirection='y'
draggable="touch"
limit={10}
theme="colored"
transition={Slide}
className={styles.customContainer}
className={styles.wideToastContainer}
closeButton={false}
/>
);
+4 -4
View File
@@ -20,7 +20,7 @@
}
.customContainer {
.wideToastContainer {
width: auto; /* Ensure the width is auto to respect max-width */
padding: 10px; /* Add padding to ensure space on all sides */
left: 5vw; /* Add space on the left */
@@ -29,14 +29,14 @@
transform: none; /* Remove transform */
border-radius: var(--ifm-global-radius); /* Ensure the border-radius is respected */
}
.customToast {
.wideToast {
border-radius: var(--ifm-global-radius); /* Ensure the border-radius is respected */
justify-content: center; /* Center content horizontally */
box-shadow: var(--box-shadow); /* Use CSS variable */
}
@media only screen and (max-width : 480px) {
.customContainer {
.wideToastContainer {
width: auto; /* Ensure the width is auto to respect max-width */
padding: 10px; /* Add padding to ensure space on all sides */
left: 5vw; /* Add space on the left */
@@ -45,7 +45,7 @@
transform: none; /* Remove transform */
border-radius: var(--ifm-global-radius); /* Ensure the border-radius is respected */
}
.customToast {
.wideToast {
border-radius: var(--ifm-global-radius); /* Ensure the border-radius is respected */
justify-content: center; /* Center content horizontally */
+32
View File
@@ -102,3 +102,35 @@
.hidden-tabs {
display: none;
}
.default-themed-button {
background: var(--ifm-color-primary);
color: white;
border: none;
padding: 15px;
cursor: pointer;
border-radius: var(--ifm-global-radius);
margin: 0 auto;
box-shadow: var(--button-box-shadow);
transition: transform 0.2s, box-shadow 0.2s, background 0.2s;
}
.default-themed-button:disabled {
background: #ccc;
cursor: not-allowed;
box-shadow: none;
}
.default-themed-button:disabled:hover {
transform: none;
box-shadow: none;
}
.default-themed-button:hover {
transform: scale(1.05);
box-shadow: var(--button-hover-box-shadow);
color: white;
}
+2 -2
View File
@@ -1,12 +1,12 @@
import React from 'react';
import Footer from '@theme-original/Footer';
import { MyToastContainer } from '@site/src/components/Toasts';
import { WideToastContainer } from '@site/src/components/Toasts';
export default function FooterWrapper(props) {
return (
<>
<section>
<MyToastContainer />
<WideToastContainer />
</section>
<Footer {...props} />
</>
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB