{ "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! Login to Spotify" @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! Login to Spotify" @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 ``` ## 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 {} }: 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.