Mastodon

funkwhale+Spotify歌单同步脚本+“Spotify下载器”

funkwhale+Spotify歌单同步脚本+“Spotify下载器”
Photo by Imtiyaz Ali / Unsplash

前言

音乐是我们生活中十分重要的娱乐方式,既可以作为我们专心做事的背景音,也可以作为细细品味的艺术作品

今天为大家带来funkwhale+Spotify歌单同步脚本+“Spotify下载器”

  1. funkwhale实现联邦宇宙分享音乐(类似peertube)
  2. 将无法分享的Spotify“已喜欢的歌曲”同步到另一个可分享的歌单
  3. 使用docker部署“下载Spotify音乐”webUI

部署funkwhale基本流程

推荐使用docker的方式部署,可以参考官方部署:

Install Funkwhale using Docker — funkwhale 1.4.0 documentation

如果你不想要那么复杂,可以看看我的配置文件

  1. cloudflare tunnel反向代理
  2. cloudflare R2存储媒体文件
  3. 8087作为docker外部端口
  4. smtp.gmail.com作为SMTP服务器
nano compose.yaml
nano.env
services:
  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 -d

Spotify歌单同步脚本

众所周知,Spotify的默认喜欢歌单是无法生成分享链接的,这样我就无法获取链接下载音乐了

换一个思路,不如把“已点赞的歌曲”同步到一个公开的歌单“同步已点赞的歌曲”

由于我已经在Spotify developer dashboard(点击右上角头像——dashboard)创建过APP mastodon-music-bot,直接用它的client id和client secret

Home | Spotify for Developers

创建一个新的playlist,命名为同步已点赞的歌曲(或任何你喜欢的名字)

使用vscode在一个目录下创建spotify-sync-liked-music.py内容如下,需要自行填写

  1. CLIENT_ID
  2. CLIETN_SECRET
  3. 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 下,以艺术家为分类目录

只需要更改

  1. SPOTIFY_CLIENT_ID
  2. 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代表别人可以直接执行下载音乐命令,强烈建议自己添加一个登录验证