First commit

This commit is contained in:
2025-12-07 14:35:40 +02:00
commit 35c54263a9
15 changed files with 1026 additions and 0 deletions

12
.envrc Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
BQBNPC2WwzpQqZTz_CO2pjKj16j0hMKLGBv0xEQbUD1Ezf7e4fUT1oj2YKH7cyfRv5WiJ5TtQiNhBDfSONPgfnwfNV1IXkVWiNveGWCOkgmlGyV2wJV6TbXZEGjLFCMwpZZTR2GdP8vswc_veA2MH-T5A-Uooz3DYADjtwZz3_WuHN2W-6XwVCRHfRd4EPwG2FwtHvsC2GmZIILMYU7g0YFbDWnXIgGafA2_wEl7S-3Yuon2lNMfmmoc0LHBfupwyxdn9oD-Og

450
current Normal file
View 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, youll 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)
Youll 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
View 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
View 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
View 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
View 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
View 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
View File

12
openssl.cnf Normal file
View 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
View 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
View 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
View 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, youll 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)
Youll 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.