funkwhale+Spotify歌单同步脚本+“Spotify下载器”
前言
音乐是我们生活中十分重要的娱乐方式,既可以作为我们专心做事的背景音,也可以作为细细品味的艺术作品
今天为大家带来funkwhale+Spotify歌单同步脚本+“Spotify下载器”
- funkwhale实现联邦宇宙分享音乐(类似peertube)
- 将无法分享的Spotify“已喜欢的歌曲”同步到另一个可分享的歌单
- 使用docker部署“下载Spotify音乐”webUI
部署funkwhale基本流程

推荐使用docker的方式部署,可以参考官方部署:
Install Funkwhale using Docker — funkwhale 1.4.0 documentation
如果你不想要那么复杂,可以看看我的配置文件
- cloudflare tunnel反向代理
- cloudflare R2存储媒体文件
- 8087作为docker外部端口
- smtp.gmail.com作为SMTP服务器
nano compose.yaml
nano.envservices:
postgres:
container_name: funkwhale_postgres
image: postgres:15-alpine
restart: unless-stopped
env_file: .env
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=
- POSTGRES_DB=funkwhale
- POSTGRES_HOST_AUTH_METHOD=trust
volumes:
- ./data/postgres:/var/lib/postgresql/data
redis:
container_name: funkwhale_redis
image: redis:7-alpine
restart: unless-stopped
env_file: .env
volumes:
- ./data/redis:/data
celeryworker:
container_name: funkwhale_celeryworker
image: funkwhale/api:${FUNKWHALE_VERSION:-latest}
restart: unless-stopped
depends_on:
- postgres
- redis
env_file: .env
command:
- celery
- --app=funkwhale_api.taskapp
- worker
- --loglevel=INFO
- --concurrency=${CELERYD_CONCURRENCY-0}
environment:
- C_FORCE_ROOT=true
volumes:
- "${MUSIC_DIRECTORY_SERVE_PATH}:${MUSIC_DIRECTORY_PATH}:ro"
- "${MEDIA_ROOT}:${MEDIA_ROOT}"
celerybeat:
container_name: funkwhale_celerybeat
image: funkwhale/api:${FUNKWHALE_VERSION:-latest}
restart: unless-stopped
depends_on:
- postgres
- redis
env_file: .env
command:
- celery
- --app=funkwhale_api.taskapp
- beat
- --loglevel=INFO
api:
container_name: funkwhale_api
image: funkwhale/api:${FUNKWHALE_VERSION:-latest}
restart: unless-stopped
depends_on:
- postgres
- redis
env_file: .env
volumes:
- "${MUSIC_DIRECTORY_SERVE_PATH}:${MUSIC_DIRECTORY_PATH}:ro"
- "${MEDIA_ROOT}:${MEDIA_ROOT}"
- "${STATIC_ROOT}:${STATIC_ROOT}"
ports:
- "${FUNKWHALE_API_IP}:${FUNKWHALE_API_PORT}:5000"
front:
container_name: funkwhale_front
image: funkwhale/front:${FUNKWHALE_VERSION:-latest}
restart: unless-stopped
depends_on:
- api
env_file: .env
environment:
- "NGINX_MAX_BODY_SIZE=${NGINX_MAX_BODY_SIZE}"
volumes:
- "${MUSIC_DIRECTORY_SERVE_PATH}:${MUSIC_DIRECTORY_PATH}:ro"
- "${MEDIA_ROOT}:${MEDIA_ROOT}:ro"
- "${STATIC_ROOT}:/usr/share/nginx/html/staticfiles:ro"
ports:
- "8087:80"
# ===== Funkwhale 基础 =====
FUNKWHALE_VERSION=latest
FUNKWHALE_API_IP=127.0.0.1
FUNKWHALE_API_PORT=5000
FUNKWHALE_HOSTNAME=funkwhale.feddit.social
FUNKWHALE_PROTOCOL=https
FUNKWHALE_WEB_WORKERS=8
LOGLEVEL=error
DJANGO_SECRET_KEY=
# 可使用 `openssl rand -base64 45` 生成
# ===== 邮件 =====
[email protected]
EMAIL_PASS=xxxxxxxxxxxxxxxx
EMAIL_CONFIG=smtp+tls://${EMAIL_USER}:${EMAIL_PASS}@smtp.gmail.com:587
[email protected]
ACCOUNT_EMAIL_VERIFICATION_ENFORCE=false
# ===== 路径 =====
MEDIA_ROOT=/home/jay/docker/social/funkwhale/data/media
STATIC_ROOT=/home/jay/docker/social/funkwhale/data/static
MUSIC_DIRECTORY_PATH=/home/jay/docker/social/funkwhale/data/music
MUSIC_DIRECTORY_SERVE_PATH=/home/jay/docker/social/funkwhale/data/music
FUNKWHALE_FRONTEND_PATH=/home/jay/docker/social/funkwhale/front/dist
NGINX_MAX_BODY_SIZE=100M
# ===== S3 外部存储 =====
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_STORAGE_BUCKET_NAME=funkwhale
AWS_S3_ENDPOINT_URL=https://id.r2.cloudflarestorage.com
PROXY_MEDIA=false
AWS_S3_REGION_NAME=auto
AWS_S3_CUSTOM_DOMAIN=files.funkwhale.feddit.social
docker compose up -dSpotify歌单同步脚本
众所周知,Spotify的默认喜欢歌单是无法生成分享链接的,这样我就无法获取链接下载音乐了
换一个思路,不如把“已点赞的歌曲”同步到一个公开的歌单“同步已点赞的歌曲”。
由于我已经在Spotify developer dashboard(点击右上角头像——dashboard)创建过APP mastodon-music-bot,直接用它的client id和client secret
Home | Spotify for Developers
创建一个新的playlist,命名为同步已点赞的歌曲(或任何你喜欢的名字)
使用vscode在一个目录下创建spotify-sync-liked-music.py内容如下,需要自行填写
- CLIENT_ID
- CLIETN_SECRET
- TARGET_PLAYLIST_ID
import spotipy # type: ignore
from spotipy.oauth2 import SpotifyOAuth # type: ignore
# ================= 配置 =================
CLIENT_ID = ""
CLIENT_SECRET = ""
REDIRECT_URI = "http://127.0.0.1:8888/callback"
SCOPE = "playlist-read-private playlist-modify-private playlist-modify-public user-library-read"
# 目标播放列表 ID
TARGET_PLAYLIST_ID = ""
# ================= 初始化 =================
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
redirect_uri=REDIRECT_URI,
scope=SCOPE
))
# ================= 获取 Liked Songs =================
liked_tracks = []
results = sp.current_user_saved_tracks(limit=50)
while results:
liked_tracks.extend([item['track']['uri'] for item in results['items']])
if results['next']:
results = sp.next(results)
else:
results = None
print(f"Total liked songs: {len(liked_tracks)}")
# ================= 获取目标播放列表已有歌曲 =================
existing_tracks = []
results = sp.playlist_items(TARGET_PLAYLIST_ID, limit=100)
while results:
existing_tracks.extend([item['track']['uri'] for item in results['items']])
if results['next']:
results = sp.next(results)
else:
results = None
print(f"Existing tracks in target playlist: {len(existing_tracks)}")
# ================= 筛选新歌曲 =================
new_tracks = [t for t in liked_tracks if t not in existing_tracks]
print(f"New tracks to add: {len(new_tracks)}")
# ================= 分批添加 =================
BATCH_SIZE = 100
for i in range(0, len(new_tracks), BATCH_SIZE):
batch = new_tracks[i:i + BATCH_SIZE]
sp.playlist_add_items(TARGET_PLAYLIST_ID, batch)
print(f"Added batch {i//BATCH_SIZE + 1}: {len(batch)} tracks")
print("同步完成 ✅")
source venv/bin/activate
python3 spotify-sync-liked-music.py
完成验证,返回查看,歌曲已全部同步

音乐下载我使用Spotify-saver项目
GitHub - gabrielbaute/spotify-saver: Download your Spotify albums from YoutubeMusic to your JellyFin music library.
Download your Spotify albums from YoutubeMusic to your JellyFin music library. - gabrielbaute/spotify-saver
他的工作原理是:从 YouTube Music 下载带有 Spotify 元数据的音频
如图,话费一个小时,为我下载好了787首歌曲在~/docker/downloader/spotify-saver/music 下,以艺术家为分类目录

只需要更改
- SPOTIFY_CLIENT_ID
- SPOTIFY_CLIENT_SECRET
git clone https://github.com/gabrielbaute/spotify-saver.git
cd spotify-saver
mkdir -p music config
cat > .env << EOF
SPOTIFY_CLIENT_ID=XXX
SPOTIFY_CLIENT_SECRET=XXX
SPOTIFY_REDIRECT_URI=http://127.0.0.1:8000/callback
EOF由于默认没用登录,所以暴露url代表别人可以直接执行下载音乐命令,强烈建议自己添加一个登录验证