First commit
This commit is contained in:
12
.envrc
Normal file
12
.envrc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export DIRENV_WARN_TIMEOUT=20s
|
||||||
|
|
||||||
|
eval "$(devenv direnvrc)"
|
||||||
|
|
||||||
|
# `use devenv` supports the same options as the `devenv shell` command.
|
||||||
|
#
|
||||||
|
# To silence all output, use `--quiet`.
|
||||||
|
#
|
||||||
|
# Example usage: use devenv --quiet --impure --option services.postgres.enable:bool true
|
||||||
|
use devenv
|
||||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Devenv
|
||||||
|
.devenv*
|
||||||
|
devenv.local.nix
|
||||||
|
devenv.local.yaml
|
||||||
|
|
||||||
|
# direnv
|
||||||
|
.direnv
|
||||||
|
|
||||||
|
# pre-commit
|
||||||
|
.pre-commit-config.yaml
|
||||||
8
README.md
Normal file
8
README.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Spotify liked playlist downloader
|
||||||
|
|
||||||
|
- [X] Working SpotifyAPI with ngrok+HTTPS
|
||||||
|
- [ ] Fix this in the openssl call:
|
||||||
|
/nix/store/dzz1xf4nxx8v42pz5sxxx62r0jjpl0qf-craftcerts-script: line 5: 2: command not found
|
||||||
|
req: Extra option: "1"
|
||||||
|
req: Use -help for summary.
|
||||||
|
- [ ] Make geturl.py needs to return playlist URL to the program
|
||||||
1
access_token.txt
Normal file
1
access_token.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
BQBNPC2WwzpQqZTz_CO2pjKj16j0hMKLGBv0xEQbUD1Ezf7e4fUT1oj2YKH7cyfRv5WiJ5TtQiNhBDfSONPgfnwfNV1IXkVWiNveGWCOkgmlGyV2wJV6TbXZEGjLFCMwpZZTR2GdP8vswc_veA2MH-T5A-Uooz3DYADjtwZz3_WuHN2W-6XwVCRHfRd4EPwG2FwtHvsC2GmZIILMYU7g0YFbDWnXIgGafA2_wEl7S-3Yuon2lNMfmmoc0LHBfupwyxdn9oD-Og
|
||||||
450
current
Normal file
450
current
Normal file
@@ -0,0 +1,450 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"devenv": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "src/modules",
|
||||||
|
"lastModified": 1764927628,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "247d7027f91368054fb0eefbd755a73d42b66fee",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "src/modules",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1764712249,
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "3b279e4317ccfa4865356387935310531357d919",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1765016596,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"git-hooks",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1762808025,
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1764580874,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"rev": "dcf61356c3ab25f1362b4a4428a6d871e84f1d1d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "rolling",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": "devenv",
|
||||||
|
"git-hooks": "git-hooks",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"pre-commit-hooks": [
|
||||||
|
"git-hooks"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
|
{ pkgs, lib, config, inputs, ... }: {
|
||||||
|
env.GREET = "SpotDL devenv";
|
||||||
|
packages = [pkgs.spotdl pkgs.python313Packages.spotipy pkgs.python313Packages.flask pkgs.openssl];
|
||||||
|
|
||||||
|
scripts.hello.exec = ''echo hello from $GREET ! '';
|
||||||
|
|
||||||
|
scripts.craftcerts.exec = ''
|
||||||
|
mkdir -p ssl # Use -p to avoid error if the directory exists
|
||||||
|
cd ssl
|
||||||
|
if [ ! -f ../openssl.cnf ]; then
|
||||||
|
echo "Please create 'openssl.cnf' in the parent directory."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.cert -days 365 -nodes -config ../openssl.cnf >/dev/null
|
||||||
|
'';
|
||||||
|
|
||||||
|
scripts.download.exec = ''
|
||||||
|
while true; do
|
||||||
|
url=$(python ./geturl.py) # Capture the output of geturl.py
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
spotdl "$url"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "Error downloading, retrying..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
|
||||||
|
enterShell = ''
|
||||||
|
hello
|
||||||
|
craftcerts
|
||||||
|
download
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
|
||||||
|
inputs:
|
||||||
|
nixpkgs:
|
||||||
|
url: github:cachix/devenv-nixpkgs/rolling
|
||||||
|
|
||||||
|
# If you're using non-OSS software, you can set allowUnfree to true.
|
||||||
|
# allowUnfree: true
|
||||||
|
|
||||||
|
# If you're willing to use a package that's vulnerable
|
||||||
|
# permittedInsecurePackages:
|
||||||
|
# - "openssl-1.1.1w"
|
||||||
|
|
||||||
|
# If you have more than one devenv you can merge them
|
||||||
|
#imports:
|
||||||
|
# - ./backend
|
||||||
|
from flask import Flask, request, redirect
|
||||||
|
import spotipy
|
||||||
|
from spotipy.oauth2 import SpotifyOAuth
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
client_id = "c0020cc0e05245efb2ddb61b7045e4f2"
|
||||||
|
client_secret = "2e2e7c98f849403fbacc219c1f01c17d"
|
||||||
|
redirect_uri = "https://localhost:8888/callback"
|
||||||
|
|
||||||
|
sp_oauth = SpotifyOAuth(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, scope="playlist-modify-private user-library-read")
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return "Welcome! <a href='/login'>Login to Spotify</a>"
|
||||||
|
|
||||||
|
@app.route('/login')
|
||||||
|
def login():
|
||||||
|
return redirect(sp_oauth.get_authorize_url())
|
||||||
|
|
||||||
|
@app.route('/callback')
|
||||||
|
def callback():
|
||||||
|
code = request.args.get('code')
|
||||||
|
token_info = sp_oauth.get_access_token(code)
|
||||||
|
access_token = token_info['access_token']
|
||||||
|
sp = spotipy.Spotify(auth=access_token)
|
||||||
|
|
||||||
|
print("Fetching liked songs...")
|
||||||
|
liked_songs = []
|
||||||
|
results = sp.current_user_saved_tracks()
|
||||||
|
|
||||||
|
while results:
|
||||||
|
for item in results['items']:
|
||||||
|
liked_songs.append(item['track']['id'])
|
||||||
|
if results['next']:
|
||||||
|
results = sp.next(results)
|
||||||
|
time.sleep(1)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
playlist_name = datetime.now().strftime("Liked Songs Collection - %Y-%m-%d %H-%M-%S")
|
||||||
|
user_id = sp.me()['id']
|
||||||
|
new_playlist = sp.user_playlist_create(user_id, playlist_name, public=False)
|
||||||
|
|
||||||
|
print(f"Created playlist: {playlist_name}")
|
||||||
|
|
||||||
|
for i in range(0, len(liked_songs), 100):
|
||||||
|
batch = liked_songs[i:i + 100]
|
||||||
|
sp.playlist_add_items(new_playlist['id'], batch)
|
||||||
|
print(f"Added {len(batch)} songs...")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
playlist_url = new_playlist['external_urls']['spotify']
|
||||||
|
print(f"Playlist URL: {playlist_url}")
|
||||||
|
|
||||||
|
# Write playlist URL for the download script
|
||||||
|
with open("last_playlist_url.txt", "w") as f:
|
||||||
|
f.write(playlist_url)
|
||||||
|
|
||||||
|
return f"{playlist_url}"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Starting Flask server...")
|
||||||
|
os.execvp('flask', [
|
||||||
|
'flask',
|
||||||
|
'run',
|
||||||
|
'--host=0.0.0.0',
|
||||||
|
'--port=8888',
|
||||||
|
'--cert=ssl/server.cert',
|
||||||
|
'--key=ssl/server.key'
|
||||||
|
])
|
||||||
|
from flask import Flask, request, redirect
|
||||||
|
import spotipy
|
||||||
|
from spotipy.oauth2 import SpotifyOAuth
|
||||||
|
import threading
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time # Import time module
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
client_id = "c0020cc0e05245efb2ddb61b7045e4f2"
|
||||||
|
client_secret = "2e2e7c98f849403fbacc219c1f01c17d"
|
||||||
|
redirect_uri = "https://localhost:8888/callback"
|
||||||
|
|
||||||
|
sp_oauth = SpotifyOAuth(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, scope="playlist-modify-private user-library-read")
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return "Welcome! <a href='/login'>Login to Spotify</a>"
|
||||||
|
|
||||||
|
@app.route('/login')
|
||||||
|
def login():
|
||||||
|
return redirect(sp_oauth.get_authorize_url())
|
||||||
|
|
||||||
|
@app.route('/callback')
|
||||||
|
def callback():
|
||||||
|
code = request.args.get('code')
|
||||||
|
token_info = sp_oauth.get_access_token(code)
|
||||||
|
access_token = token_info['access_token']
|
||||||
|
|
||||||
|
with open("access_token.txt", "w") as f:
|
||||||
|
f.write(access_token)
|
||||||
|
|
||||||
|
print("Authorization complete. You can close this window.", file=sys.stderr)
|
||||||
|
return "Authorization complete. You can close this window."
|
||||||
|
|
||||||
|
def create_playlist():
|
||||||
|
if not os.path.exists("access_token.txt"):
|
||||||
|
print("No access token found.", file=sys.stderr)
|
||||||
|
return
|
||||||
|
|
||||||
|
with open("access_token.txt", "r") as f:
|
||||||
|
access_token = f.read().strip()
|
||||||
|
|
||||||
|
sp = spotipy.Spotify(auth=access_token)
|
||||||
|
|
||||||
|
print("Fetching liked songs...", file=sys.stderr)
|
||||||
|
liked_songs = []
|
||||||
|
results = sp.current_user_saved_tracks()
|
||||||
|
|
||||||
|
while results:
|
||||||
|
for item in results['items']:
|
||||||
|
liked_songs.append(item['track']['id'])
|
||||||
|
if results['next']:
|
||||||
|
results = sp.next(results)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
playlist_name = datetime.now().strftime("Liked Songs Collection - %Y-%m-%d %H-%M-%S")
|
||||||
|
user_id = sp.me()['id']
|
||||||
|
new_playlist = sp.user_playlist_create(user_id, playlist_name, public=False)
|
||||||
|
|
||||||
|
print(f"Created playlist: {playlist_name}", file=sys.stderr)
|
||||||
|
|
||||||
|
for i in range(0, len(liked_songs), 100):
|
||||||
|
batch = liked_songs[i:i + 100]
|
||||||
|
sp.playlist_add_items(new_playlist['id'], batch)
|
||||||
|
print(f"Added {len(batch)} songs...", file=sys.stderr)
|
||||||
|
|
||||||
|
playlist_url = new_playlist['external_urls']['spotify']
|
||||||
|
print(f"Playlist URL: {playlist_url}", file=sys.stderr)
|
||||||
|
|
||||||
|
return playlist_url
|
||||||
|
|
||||||
|
def run_flask():
|
||||||
|
app.run(ssl_context=('ssl/server.cert', 'ssl/server.key'), host="0.0.0.0", port=8888)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Start the Flask app in a background thread
|
||||||
|
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
||||||
|
flask_thread.start()
|
||||||
|
|
||||||
|
# Inform the user to authorize access via the URL shown in the terminal
|
||||||
|
print("Please authorize access via the following URL:", sp_oauth.get_authorize_url())
|
||||||
|
|
||||||
|
# Wait until the access token is available
|
||||||
|
while not os.path.exists("access_token.txt"):
|
||||||
|
time.sleep(1) # Sleep for a while, then check again
|
||||||
|
|
||||||
|
# Now that we have the token, proceed with creating the playlist
|
||||||
|
playlist_url = create_playlist()
|
||||||
|
print(f"Playlist URL returned: {playlist_url}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
[req]
|
||||||
|
default_bits = 2048
|
||||||
|
distinguished_name = req_distinguished_name
|
||||||
|
prompt = no
|
||||||
|
[req_distinguished_name]
|
||||||
|
C = US
|
||||||
|
ST = California
|
||||||
|
L = San Francisco
|
||||||
|
O = Example Company
|
||||||
|
OU = IT Department
|
||||||
|
CN = localhost
|
||||||
|
emailAddress = email@example.com
|
||||||
|
To implement HTTPS using Let's Encrypt and ngrok while modifying your Flask application, follow these structured steps:
|
||||||
|
|
||||||
|
## Step 1: Install and Set Up ngrok
|
||||||
|
|
||||||
|
1. **Download ngrok**:
|
||||||
|
- Go to the [ngrok website](https://ngrok.com/download) and download the appropriate version for your OS.
|
||||||
|
- Install ngrok by following the instructions on the site.
|
||||||
|
|
||||||
|
2. **Authenticate ngrok**:
|
||||||
|
- Sign up for a free account at ngrok.com.
|
||||||
|
- After signing up, you’ll receive an authtoken. Run the following command to set it up:
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your_auth_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Modify Flask to Accept HTTPS
|
||||||
|
|
||||||
|
Update your Flask application to use ngrok to tunnel HTTPS traffic:
|
||||||
|
|
||||||
|
1. **Change `run_flask()` to bind to HTTP**:
|
||||||
|
This is so ngrok can handle the SSL termination. Update the endpoint by removing the `ssl_context`:
|
||||||
|
```python
|
||||||
|
def run_flask():
|
||||||
|
app.run(host="0.0.0.0", port=8888)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Expose the Flask app via ngrok**:
|
||||||
|
You can run ngrok to expose your Flask app by executing:
|
||||||
|
```bash
|
||||||
|
ngrok http 8888
|
||||||
|
```
|
||||||
|
This will give you a public HTTPS URL that tunnels to your local Flask app.
|
||||||
|
|
||||||
|
## Step 3: Modify the Development Environment (devenv.nix)
|
||||||
|
|
||||||
|
You’ll need to add the ngrok installation to your `devenv.nix` configuration, if it's not already installed.
|
||||||
|
|
||||||
|
1. **Edit `devenv.nix`**:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
pkgs.mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.ngrok
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
# Set up ngrok to run automatically
|
||||||
|
ngrok http 8888 &
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This snippet sets up ngrok to run in the background whenever you start your development environment.
|
||||||
|
|
||||||
|
## Step 4: Implement the PKI Chain in `geturl.py`
|
||||||
|
|
||||||
|
To implement an HTTPS PKI chain, consider using the Let's Encrypt certificate. For local development, you can't use Let's Encrypt directly without a publicly reachable domain, but you can still prepare your code as if you're using secure connections.
|
||||||
|
|
||||||
|
1. **Modify `geturl.py`**:
|
||||||
|
|
||||||
|
Assuming your application logic has not changed, implement a function to fetch the ngrok URL:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
def get_ngrok_url():
|
||||||
|
response = requests.get("http://localhost:4040/api/tunnels")
|
||||||
|
tunnels = response.json()["tunnels"]
|
||||||
|
for tunnel in tunnels:
|
||||||
|
if tunnel["name"] == "http":
|
||||||
|
return tunnel["public_url"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_playlist():
|
||||||
|
# Your existing code to create a playlist
|
||||||
|
...
|
||||||
|
|
||||||
|
def run_flask():
|
||||||
|
app.run(host="0.0.0.0", port=8888)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Start Flask in a background thread
|
||||||
|
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
||||||
|
flask_thread.start()
|
||||||
|
|
||||||
|
# Wait for ngrok to be ready
|
||||||
|
time.sleep(5) # Allow some time for ngrok to initialize
|
||||||
|
|
||||||
|
ngrok_url = get_ngrok_url()
|
||||||
|
print(f"Ngrok URL: {ngrok_url}")
|
||||||
|
|
||||||
|
print("Please authorize access via the following URL:", sp_oauth.get_authorize_url())
|
||||||
|
|
||||||
|
while not os.path.exists("access_token.txt"):
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
playlist_url = create_playlist()
|
||||||
|
print(f"Playlist URL returned: {playlist_url}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
In this code:
|
||||||
|
- `get_ngrok_url()` fetches the ngrok tunnel URL dynamically.
|
||||||
|
- You can then safely use this URL to inform other services of your HTTPS endpoint.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This setup will let you run a local Flask application using HTTPS through an ngrok tunnel. You process the playlist creation and output the URL correctly while working in a secure environment. If you later transition to production, you can directly implement Let's Encrypt for your domain, maintaining the same architecture with minimal changes.
|
||||||
103
devenv.lock
Normal file
103
devenv.lock
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"devenv": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "src/modules",
|
||||||
|
"lastModified": 1764927628,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "247d7027f91368054fb0eefbd755a73d42b66fee",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "src/modules",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1764712249,
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "3b279e4317ccfa4865356387935310531357d919",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1765016596,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"git-hooks",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1762808025,
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1764580874,
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"rev": "dcf61356c3ab25f1362b4a4428a6d871e84f1d1d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "rolling",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": "devenv",
|
||||||
|
"git-hooks": "git-hooks",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"pre-commit-hooks": [
|
||||||
|
"git-hooks"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
38
devenv.nix
Normal file
38
devenv.nix
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{ pkgs, lib, config, inputs, ... }: {
|
||||||
|
env.GREET = "SpotDL devenv";
|
||||||
|
packages = [pkgs.spotdl pkgs.python313Packages.spotipy pkgs.python313Packages.flask pkgs.openssl pkgs.ngrok ];
|
||||||
|
|
||||||
|
scripts.hello.exec = ''echo hello from $GREET ! '';
|
||||||
|
|
||||||
|
scripts.craftcerts.exec = ''
|
||||||
|
mkdir -p ssl # Use -p to avoid error if the directory exists
|
||||||
|
cd ssl
|
||||||
|
if [ ! -f ./openssl.cnf ]; then
|
||||||
|
echo "Creating openssl..."
|
||||||
|
openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.cert -days 365 -nodes -config ../openssl.cnf 1&2 > /dev/null
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
scripts.download.exec = ''
|
||||||
|
ngrok http 8888 > /dev/null &
|
||||||
|
python ./geturl.py > geturl_logfile
|
||||||
|
url=$(cat -p geturl_logfile) # Capture the output of geturl.py
|
||||||
|
echo "STATEMENT AFTER GETURL"
|
||||||
|
while true; do
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "SPOTDL ATTEMPT:"
|
||||||
|
spotdl "$url"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "Error downloading, retrying..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
|
||||||
|
enterShell = ''
|
||||||
|
hello
|
||||||
|
craftcerts
|
||||||
|
download
|
||||||
|
'';
|
||||||
|
}
|
||||||
15
devenv.yaml
Normal file
15
devenv.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
|
||||||
|
inputs:
|
||||||
|
nixpkgs:
|
||||||
|
url: github:cachix/devenv-nixpkgs/rolling
|
||||||
|
|
||||||
|
# If you're using non-OSS software, you can set allowUnfree to true.
|
||||||
|
# allowUnfree: true
|
||||||
|
|
||||||
|
# If you're willing to use a package that's vulnerable
|
||||||
|
# permittedInsecurePackages:
|
||||||
|
# - "openssl-1.1.1w"
|
||||||
|
|
||||||
|
# If you have more than one devenv you can merge them
|
||||||
|
#imports:
|
||||||
|
# - ./backend
|
||||||
75
geturl.old
Normal file
75
geturl.old
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
from flask import Flask, request, redirect
|
||||||
|
import spotipy
|
||||||
|
from spotipy.oauth2 import SpotifyOAuth
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
client_id = "c0020cc0e05245efb2ddb61b7045e4f2"
|
||||||
|
client_secret = "2e2e7c98f849403fbacc219c1f01c17d"
|
||||||
|
redirect_uri = "https://localhost:8888/callback"
|
||||||
|
|
||||||
|
sp_oauth = SpotifyOAuth(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, scope="playlist-modify-private user-library-read")
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return "Welcome! <a href='/login'>Login to Spotify</a>"
|
||||||
|
|
||||||
|
@app.route('/login')
|
||||||
|
def login():
|
||||||
|
return redirect(sp_oauth.get_authorize_url())
|
||||||
|
|
||||||
|
@app.route('/callback')
|
||||||
|
def callback():
|
||||||
|
code = request.args.get('code')
|
||||||
|
token_info = sp_oauth.get_access_token(code)
|
||||||
|
access_token = token_info['access_token']
|
||||||
|
sp = spotipy.Spotify(auth=access_token)
|
||||||
|
|
||||||
|
print("Fetching liked songs...")
|
||||||
|
liked_songs = []
|
||||||
|
results = sp.current_user_saved_tracks()
|
||||||
|
|
||||||
|
while results:
|
||||||
|
for item in results['items']:
|
||||||
|
liked_songs.append(item['track']['id'])
|
||||||
|
if results['next']:
|
||||||
|
results = sp.next(results)
|
||||||
|
time.sleep(1)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
playlist_name = datetime.now().strftime("Liked Songs Collection - %Y-%m-%d %H-%M-%S")
|
||||||
|
user_id = sp.me()['id']
|
||||||
|
new_playlist = sp.user_playlist_create(user_id, playlist_name, public=False)
|
||||||
|
|
||||||
|
print(f"Created playlist: {playlist_name}")
|
||||||
|
|
||||||
|
for i in range(0, len(liked_songs), 100):
|
||||||
|
batch = liked_songs[i:i + 100]
|
||||||
|
sp.playlist_add_items(new_playlist['id'], batch)
|
||||||
|
print(f"Added {len(batch)} songs...")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
playlist_url = new_playlist['external_urls']['spotify']
|
||||||
|
print(f"Playlist URL: {playlist_url}")
|
||||||
|
|
||||||
|
# Write playlist URL for the download script
|
||||||
|
with open("last_playlist_url.txt", "w") as f:
|
||||||
|
f.write(playlist_url)
|
||||||
|
|
||||||
|
return f"{playlist_url}"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Starting Flask server...")
|
||||||
|
os.execvp('flask', [
|
||||||
|
'flask',
|
||||||
|
'run',
|
||||||
|
'--host=0.0.0.0',
|
||||||
|
'--port=8888',
|
||||||
|
'--cert=ssl/server.cert',
|
||||||
|
'--key=ssl/server.key'
|
||||||
|
])
|
||||||
135
geturl.py
Normal file
135
geturl.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
from flask import Flask, request, redirect
|
||||||
|
import spotipy
|
||||||
|
from spotipy.oauth2 import SpotifyOAuth
|
||||||
|
import threading
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
from datetime import datetime
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
stop_flask = threading.Event()
|
||||||
|
|
||||||
|
client_id = "c0020cc0e05245efb2ddb61b7045e4f2"
|
||||||
|
client_secret = "2e2e7c98f849403fbacc219c1f01c17d"
|
||||||
|
redirect_uri = "https://elease-uncapsuled-yosef.ngrok-free.dev/callback" # Hardcoded ngrok URL
|
||||||
|
|
||||||
|
sp_oauth = SpotifyOAuth(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, scope="playlist-modify-private user-library-read")
|
||||||
|
|
||||||
|
# This will store whether the access token was acquired
|
||||||
|
access_token_obtained = threading.Event()
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return "Welcome! <a href='/login'>Login to Spotify</a>"
|
||||||
|
|
||||||
|
@app.route('/login')
|
||||||
|
def login():
|
||||||
|
return redirect(sp_oauth.get_authorize_url())
|
||||||
|
|
||||||
|
@app.route('/callback')
|
||||||
|
def callback():
|
||||||
|
code = request.args.get('code')
|
||||||
|
token_info = sp_oauth.get_access_token(code)
|
||||||
|
access_token = token_info['access_token']
|
||||||
|
|
||||||
|
with open("access_token.txt", "w") as f:
|
||||||
|
f.write(access_token)
|
||||||
|
|
||||||
|
#print("Authorization complete. You can close this window.", file=sys.stderr)
|
||||||
|
|
||||||
|
# Signal that the access token has been obtained
|
||||||
|
access_token_obtained.set()
|
||||||
|
|
||||||
|
return "Authorization complete. You can close this window."
|
||||||
|
|
||||||
|
def create_playlist():
|
||||||
|
if not os.path.exists("access_token.txt"):
|
||||||
|
print("No access token found.", file=sys.stderr)
|
||||||
|
return
|
||||||
|
|
||||||
|
with open("access_token.txt", "r") as f:
|
||||||
|
access_token = f.read().strip()
|
||||||
|
|
||||||
|
sp = spotipy.Spotify(auth=access_token)
|
||||||
|
|
||||||
|
print("Fetching liked songs...", file=sys.stderr)
|
||||||
|
liked_songs = []
|
||||||
|
results = sp.current_user_saved_tracks()
|
||||||
|
|
||||||
|
while results:
|
||||||
|
for item in results['items']:
|
||||||
|
liked_songs.append(item['track']['id'])
|
||||||
|
if results['next']:
|
||||||
|
results = sp.next(results)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
playlist_name = datetime.now().strftime("Liked Songs Collection - %Y-%m-%d %H-%M-%S")
|
||||||
|
user_id = sp.me()['id']
|
||||||
|
new_playlist = sp.user_playlist_create(user_id, playlist_name, public=False)
|
||||||
|
|
||||||
|
print(f"Created playlist: {playlist_name}", file=sys.stderr)
|
||||||
|
|
||||||
|
for i in range(0, len(liked_songs), 100):
|
||||||
|
batch = liked_songs[i:i + 100]
|
||||||
|
sp.playlist_add_items(new_playlist['id'], batch)
|
||||||
|
print(f"Added {len(batch)} songs...", file=sys.stderr)
|
||||||
|
|
||||||
|
playlist_url = new_playlist['external_urls']['spotify']
|
||||||
|
print(f"Playlist URL: {playlist_url}", file=sys.stderr)
|
||||||
|
|
||||||
|
return playlist_url
|
||||||
|
|
||||||
|
def run_flask():
|
||||||
|
with open(os.devnull, 'w') as fnull:
|
||||||
|
# Redirect both stdout and stderr to devnull
|
||||||
|
sys.stdout = fnull
|
||||||
|
sys.stderr = fnull
|
||||||
|
subprocess.run(["xdg-open", "http://192.168.0.100:8888"])
|
||||||
|
app.run(host="0.0.0.0", port=8888, use_reloader=False)
|
||||||
|
# sys.stdout = sys.__stdout__ # Restore original stdout
|
||||||
|
# sys.stderr = sys.__stderr__ # Restore original stderr
|
||||||
|
|
||||||
|
def kill_ngrok():
|
||||||
|
try:
|
||||||
|
# Find ngrok process and kill it
|
||||||
|
subprocess.call(["pkill", "ngrok"])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error stopping ngrok: {str(e)}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
kill_ngrok()
|
||||||
|
|
||||||
|
# Start Flask in a background thread
|
||||||
|
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
||||||
|
flask_thread.start()
|
||||||
|
|
||||||
|
# Allow some time for Flask to start
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Start ngrok dynamically
|
||||||
|
os.system("ngrok http 8888 &")
|
||||||
|
|
||||||
|
# Allow time for ngrok to initialize
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
# Print out the hardcoded ngrok URL
|
||||||
|
print(f"Ngrok URL: {redirect_uri}", file=sys.stderr )
|
||||||
|
print("Please authorize access via the following URL:", sp_oauth.get_authorize_url(), file=sys.stderr)
|
||||||
|
|
||||||
|
# Wait for the access token to be acquired
|
||||||
|
access_token_obtained.wait() # Wait until the access token is provided
|
||||||
|
|
||||||
|
# Now that we have the token, proceed with creating the playlist
|
||||||
|
playlist_url = create_playlist()
|
||||||
|
print(playlist_url)
|
||||||
|
stop_flask.set() # Signal Flask to stop
|
||||||
|
flask_thread.join() # Wait for the Flask thread to complete
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
0
geturl_logfile
Normal file
0
geturl_logfile
Normal file
12
openssl.cnf
Normal file
12
openssl.cnf
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[req]
|
||||||
|
default_bits = 2048
|
||||||
|
distinguished_name = req_distinguished_name
|
||||||
|
prompt = no
|
||||||
|
[req_distinguished_name]
|
||||||
|
C = US
|
||||||
|
ST = California
|
||||||
|
L = San Francisco
|
||||||
|
O = Example Company
|
||||||
|
OU = IT Department
|
||||||
|
CN = localhost
|
||||||
|
emailAddress = email@example.com
|
||||||
24
ssl/server.cert
Normal file
24
ssl/server.cert
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID9TCCAt2gAwIBAgIUOI3ulMeyDqE64sr8SIkZ9U7SmDIwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
|
||||||
|
DA1TYW4gRnJhbmNpc2NvMRgwFgYDVQQKDA9FeGFtcGxlIENvbXBhbnkxFjAUBgNV
|
||||||
|
BAsMDUlUIERlcGFydG1lbnQxEjAQBgNVBAMMCWxvY2FsaG9zdDEgMB4GCSqGSIb3
|
||||||
|
DQEJARYRZW1haWxAZXhhbXBsZS5jb20wHhcNMjUxMjA3MTE1OTQxWhcNMjYxMjA3
|
||||||
|
MTE1OTQxWjCBojELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAU
|
||||||
|
BgNVBAcMDVNhbiBGcmFuY2lzY28xGDAWBgNVBAoMD0V4YW1wbGUgQ29tcGFueTEW
|
||||||
|
MBQGA1UECwwNSVQgRGVwYXJ0bWVudDESMBAGA1UEAwwJbG9jYWxob3N0MSAwHgYJ
|
||||||
|
KoZIhvcNAQkBFhFlbWFpbEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||||
|
ggEPADCCAQoCggEBAJZvtJTQQvIvJSxC6WMQ8L0fRvhIud3wFFZ21kaMT0Ptetjh
|
||||||
|
akdF8gdBu6RA4mI8mY/P67G6xsHxQG7rQIeGWl8ds0Y5TS1i9pGbxmim6DxEw/wr
|
||||||
|
EL3npG+bnKGMrYv2hwUg4r6+bfjvhPW9TgUCJN9clvwixAf6tSHK04epNPHmKXiv
|
||||||
|
aYaxGgWghSrTLIBXp22TiLfTYczCR8F4Banhkvm3wJkrvA2LsRCb9LHHnKVkXuw6
|
||||||
|
mFd8WdTZT3+s85D4pEJPiZ8U76tH6fJm0f8BZigibx7Ag3nPt6FeiDrlVznuzl5q
|
||||||
|
c9uPX6tu73nFA1/x141HgW1Aw8D1YR5iIzcfnbECAwEAAaMhMB8wHQYDVR0OBBYE
|
||||||
|
FEfxzkXqyVwA87RNe6CuiKzZVDsmMA0GCSqGSIb3DQEBCwUAA4IBAQBfJPkqzyvz
|
||||||
|
7vq6fqBOWzUc8swPGx54DCwjzALd4ujEOjhOQCktxrX2VesEk6B5yjNQd6VuOMHU
|
||||||
|
V8qo1q82UG3LKcCIkaFNlJ9d1H2pzBLVjsMshTPABEEiq1EKuKNX6G+wk7DyuBDH
|
||||||
|
0PW/OWGP0OaASYv+X8s/WF5JwQMdIsmDBgyeUQd9yMMiuluDqGFFhZ7xM77peqHj
|
||||||
|
I4GtM847rJn1lTdpJygACWlteBnMCeUd/cyLNA5q3LZFPa3JJ86Dwl3kdEgQtZ6F
|
||||||
|
FZ2KRQr84QUdPo2Wt+MSMrWv2dWCu66KuE4qGmhtzTbTnWa9D1fR0EV4y0XV/nza
|
||||||
|
VAFapoxF0keh
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
ssl/server.key
Normal file
28
ssl/server.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCWb7SU0ELyLyUs
|
||||||
|
QuljEPC9H0b4SLnd8BRWdtZGjE9D7XrY4WpHRfIHQbukQOJiPJmPz+uxusbB8UBu
|
||||||
|
60CHhlpfHbNGOU0tYvaRm8Zopug8RMP8KxC956Rvm5yhjK2L9ocFIOK+vm3474T1
|
||||||
|
vU4FAiTfXJb8IsQH+rUhytOHqTTx5il4r2mGsRoFoIUq0yyAV6dtk4i302HMwkfB
|
||||||
|
eAWp4ZL5t8CZK7wNi7EQm/Sxx5ylZF7sOphXfFnU2U9/rPOQ+KRCT4mfFO+rR+ny
|
||||||
|
ZtH/AWYoIm8ewIN5z7ehXog65Vc57s5eanPbj1+rbu95xQNf8deNR4FtQMPA9WEe
|
||||||
|
YiM3H52xAgMBAAECggEABvrO8mRdBczwjOacGtzCaajrx1li98hTvj0AZTitdQ+t
|
||||||
|
tmYzgtPsxj/LUaQxyLhqcMrnIulxPzZ2/Ip5VYxjESSQ8Lw3/6gGvAPh61E6p1Cy
|
||||||
|
teaG3ePDbCr4dAGhm/PDyRXw9UQDONR+mxL13as/PBaI+hDwHtVnzeYB5VqBPoui
|
||||||
|
scQ8ha8WqYrHlKskFG3dzTZQmLgajDZYB6hY+E/UYKwzCa1+GsONDkS2l8sij5pL
|
||||||
|
6zEuvcfU4WyiWZm4JyHX+v39lyjmdfhGcD/u4vKJvKZB2Ir7dKf8OdP8l+qagyBN
|
||||||
|
A11LXOmb7XkUPAK2oYNjFAAXs+go4rmcWkHSbF++wQKBgQDPqdHx5j4RQFP47/5R
|
||||||
|
4t1Pzwi+w0HBo3upi5NEfwr7Z7bbd2cH6/KcraIYYkExOke+3xtTHK1OrHTY6Nkf
|
||||||
|
/DRgrUB+ujsb4AJGm98bPrEtbVnUsfg6ssfSqEB1rYbWFhCP3wFDRlUWzzhvxt+s
|
||||||
|
cb4a8JGRJpdy1qihBrSvpGil8QKBgQC5c9wkrbSYdXLCp0hy8s9832g6hZJjH0LM
|
||||||
|
aRTwLMPlaRHGBIkb8FU1zgd2w74lRFJytF7bPFmCXKXV/BPhURvK6FelPxLIge03
|
||||||
|
6FbsiIhHEYhsY5agkSc77qz1ESo03UStes/2kscn2wHgoet9Ssz6h1ff08BoQwL5
|
||||||
|
7sGdrGOzwQKBgQCB2dqhvCsLdoILo2YPpiBlCzyYrFet0aA+ADzyE862LcA1s5AF
|
||||||
|
cBCg0CIPxUCmm57JR5E6gzALheL6z38VdQr7eNpfY+waHhTOOiFkU+tOUJZfXXmu
|
||||||
|
mqRAoVzNONibfeiVTgjoHE6QmLrdiinLFsSc69jaPpts/7UqG5tYSSH2wQKBgQCo
|
||||||
|
2y1e2CrPhmDvi5ET2LHDaUdlwakAR75ykFtYM+pKP9jHC+orXjC3xNhW8vN2yzam
|
||||||
|
6kRUKib145W7uMIBLfC4V8U12LApkOOFPC+pPseWrgghaKwFlyS6FR+2I+LiL3YQ
|
||||||
|
3vnr8MkVKPwUpFnewvTQR9tjGVLfm+Rh3Vq9TfGaAQKBgB0NX3wpmrhzDBs6C6M3
|
||||||
|
FqsiKN5vthUVeI9CCaPS13hXJArrFyV3wmc3pSA5ey5zXx46hGPYvcpUVxftFDBo
|
||||||
|
kFDu/5T7OgfqY7CtunrEcu6g6rFFjsPCxgJZpQD/WN+inp+WCAWubOu7AvNd/bwV
|
||||||
|
hAHaAz9nR9Ljz77qHEvxhmWr
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
115
whatsnext.md
Normal file
115
whatsnext.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
To implement HTTPS using Let's Encrypt and ngrok while modifying your Flask application, follow these structured steps:
|
||||||
|
|
||||||
|
## Step 1: Install and Set Up ngrok
|
||||||
|
|
||||||
|
1. **Download ngrok**:
|
||||||
|
- Go to the [ngrok website](https://ngrok.com/download) and download the appropriate version for your OS.
|
||||||
|
- Install ngrok by following the instructions on the site.
|
||||||
|
|
||||||
|
2. **Authenticate ngrok**:
|
||||||
|
- Sign up for a free account at ngrok.com.
|
||||||
|
- After signing up, you’ll receive an authtoken. Run the following command to set it up:
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your_auth_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Modify Flask to Accept HTTPS
|
||||||
|
|
||||||
|
Update your Flask application to use ngrok to tunnel HTTPS traffic:
|
||||||
|
|
||||||
|
1. **Change `run_flask()` to bind to HTTP**:
|
||||||
|
This is so ngrok can handle the SSL termination. Update the endpoint by removing the `ssl_context`:
|
||||||
|
```python
|
||||||
|
def run_flask():
|
||||||
|
app.run(host="0.0.0.0", port=8888)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Expose the Flask app via ngrok**:
|
||||||
|
You can run ngrok to expose your Flask app by executing:
|
||||||
|
```bash
|
||||||
|
ngrok http 8888
|
||||||
|
```
|
||||||
|
This will give you a public HTTPS URL that tunnels to your local Flask app.
|
||||||
|
|
||||||
|
## Step 3: Modify the Development Environment (devenv.nix)
|
||||||
|
|
||||||
|
You’ll need to add the ngrok installation to your `devenv.nix` configuration, if it's not already installed.
|
||||||
|
|
||||||
|
1. **Edit `devenv.nix`**:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
pkgs.mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.ngrok
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
# Set up ngrok to run automatically
|
||||||
|
ngrok http 8888 &
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This snippet sets up ngrok to run in the background whenever you start your development environment.
|
||||||
|
|
||||||
|
## Step 4: Implement the PKI Chain in `geturl.py`
|
||||||
|
|
||||||
|
To implement an HTTPS PKI chain, consider using the Let's Encrypt certificate. For local development, you can't use Let's Encrypt directly without a publicly reachable domain, but you can still prepare your code as if you're using secure connections.
|
||||||
|
|
||||||
|
1. **Modify `geturl.py`**:
|
||||||
|
|
||||||
|
Assuming your application logic has not changed, implement a function to fetch the ngrok URL:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
def get_ngrok_url():
|
||||||
|
response = requests.get("http://localhost:4040/api/tunnels")
|
||||||
|
tunnels = response.json()["tunnels"]
|
||||||
|
for tunnel in tunnels:
|
||||||
|
if tunnel["name"] == "http":
|
||||||
|
return tunnel["public_url"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_playlist():
|
||||||
|
# Your existing code to create a playlist
|
||||||
|
...
|
||||||
|
|
||||||
|
def run_flask():
|
||||||
|
app.run(host="0.0.0.0", port=8888)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Start Flask in a background thread
|
||||||
|
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
||||||
|
flask_thread.start()
|
||||||
|
|
||||||
|
# Wait for ngrok to be ready
|
||||||
|
time.sleep(5) # Allow some time for ngrok to initialize
|
||||||
|
|
||||||
|
ngrok_url = get_ngrok_url()
|
||||||
|
print(f"Ngrok URL: {ngrok_url}")
|
||||||
|
|
||||||
|
print("Please authorize access via the following URL:", sp_oauth.get_authorize_url())
|
||||||
|
|
||||||
|
while not os.path.exists("access_token.txt"):
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
playlist_url = create_playlist()
|
||||||
|
print(f"Playlist URL returned: {playlist_url}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
In this code:
|
||||||
|
- `get_ngrok_url()` fetches the ngrok tunnel URL dynamically.
|
||||||
|
- You can then safely use this URL to inform other services of your HTTPS endpoint.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This setup will let you run a local Flask application using HTTPS through an ngrok tunnel. You process the playlist creation and output the URL correctly while working in a secure environment. If you later transition to production, you can directly implement Let's Encrypt for your domain, maintaining the same architecture with minimal changes.
|
||||||
Reference in New Issue
Block a user