451 lines
13 KiB
Plaintext
451 lines
13 KiB
Plaintext
{
|
||
"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.
|