Загрузка...

Navidrome. Делаем личный SoundCloud для локального использования

Тема в разделе Статьи создана пользователем json 27 июн 2025. (поднята 29 июн 2025) 567 просмотров

  1. json
    json Автор темы 27 июн 2025 1446 13 янв 2023
    Все мы погрязли в мире музыкальных сервисов. Вечная реклама, подписки, запрет на загрузку музыки для прослушивания локально, слежка и остальная хрень которая в них присутствует...

    В этой статье я хочу вам рассказать про крутой self-hosted проект Navidrome, который позволяет загружать музыку на свой сервер и прослушивать ее с любых устройств. Также я дам вам бонус от себя, который улучшит ваш user experience.


    1. Подготовка сервера
    Вам для этого понадобится сервер Ubuntu/Debilian. Требуется установить Docker при помощи этой команды
    Код
    sudo apt update && \
    sudo apt install -y ca-certificates curl gnupg lsb-release && \
    sudo mkdir -p /etc/apt/keyrings && \
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
    echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
    https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list > /dev/null && \
    sudo apt update && \
    sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin && \
    sudo systemctl enable docker && \
    sudo systemctl start docker
    2. Установка Navidrome

    Для установки самого Navidrome можно использовать Docker Compose, который мы установили. Теперь можем создать отдельную аудиторию
    Код
    mkdir navidrome

    Потом зайти в файл docker-compose.yml при помощи nano
    Код
    nano docker-compose.yml

    В этот файл мы вставим наш docker-compose конфиг, при помощи которого Navidrome сразу запуститься и будет работать на сервере в отдельном контейнере
    Код
    # docker-compose.yml
    version: "3"
    services:
    navidrome:
    image: deluan/navidrome:latest
    container_name: navidrome
    ports:
    - "4533:4533"
    volumes:
    - ~/navidrome/music:/music
    - ~/navidrome/data:/data
    environment:
    - ND_LOGLEVEL=info
    - ND_SESSIONTIMEOUT=72h
    - ND_TRANSCODINGENABLED=false # чтобы не грузить процессор
    restart: unless-stopped
    Дальше для запуска нашего контейнера надо написать команду
    Код
    docker compose up -d

    Флаг
    ⁡-d
    ⁡ говорит о том, что код должен работать в фоне

    Теперь для продолжения работы вам надо перейти по адресу
    ⁡http://<IP ВАШЕГО СЕРВЕРА>:4533/

    На сайте вам предложат ввести login и password для доступа к сервису.

    3. Мой подарок

    Так как, скорее всего вы не захотите закидывать ваши песни на сервер и настраивать там метаданные файла для того, чтобы это все красиво отображалось, я сделал скрипт который работает в формате Telegram бота для загрузки песен из SoundCloud и Yandex.Music

    Python
    import os
    import shutil
    import re
    import requests
    import asyncio
    import subprocess
    from aiogram import Bot, Dispatcher, types
    from aiogram.types import FSInputFile
    from yt_dlp import YoutubeDL
    from mutagen.easyid3 import EasyID3
    from mutagen.id3 import ID3, APIC
    from shutil import copyfile

    TOKEN = "Сюда надо вставить ваш Telegram Bot Token"
    NAVIDROME_MUSIC_PATH = os.path.expanduser("~/navidrome/music")
    YANDEX_TOKEN = "Сюда надо вставить токен Yandex Music"

    bot = Bot(token=TOKEN)
    dp = Dispatcher()
    os.makedirs("downloads", exist_ok=True)

    def clean_downloads():
    for item in os.listdir("downloads"):
    full_path = os.path.join("downloads", item)
    if os.path.isfile(full_path):
    os.remove(full_path)
    elif os.path.isdir(full_path):
    shutil.rmtree(full_path)

    def safe_filename(name: str) -> str:
    return re.sub(r'[\\/*?:"<>|]', "", name).strip()

    def resolve_redirect(url: str) -> str:
    response = requests.head(url, allow_redirects=True)
    return response.url

    def download_soundcloud(url: str):
    resolved_url = resolve_redirect(url)
    ydl_opts = {
    'format': 'bestaudio/best',
    'outtmpl': 'downloads/%(uploader)s - %(title)s.%(ext)s',
    'postprocessors': [{
    'key': 'FFmpegExtractAudio',
    'preferredcodec': 'mp3',
    }],
    'writethumbnail': True,
    'prefer_ffmpeg': True,
    'quiet': True,
    'noplaylist': False,
    }

    with YoutubeDL(ydl_opts) as ydl:
    info = ydl.extract_info(resolved_url, download=True)
    entries = info.get('entries', [info])
    results = []

    for entry in entries:
    title = entry.get('title') or 'Unknown Title'
    artist = entry.get('uploader') or 'Unknown Artist'
    album = entry.get('album') or 'SoundCloud'
    final_name = safe_filename(f"{artist} - {title}")
    mp3_path = f"downloads/{final_name}.mp3"

    cover_path = None
    for ext in ['jpg', 'png', 'webp']:
    test = f"downloads/{final_name}.{ext}"
    if os.path.exists(test):
    cover_path = test
    break

    results.append({
    'mp3': mp3_path,
    'cover': cover_path,
    'artist': artist,
    'title': title,
    'album': album
    })

    return results

    def download_yandex_music(url: str):
    os.makedirs("downloads/yandex", exist_ok=True)
    subprocess.run([
    "yandex-music-downloader", "-u", url,
    "--dir", "downloads/yandex",
    "--token", YANDEX_TOKEN
    ], check=True)

    tracks = []
    for root, dirs, files in os.walk("downloads/yandex"):
    cover_path = None
    for file in files:
    if file.lower().startswith("cover") and file.lower().endswith(('.png', '.jpg', '.jpeg')):
    cover_path = os.path.join(root, file)

    for file in files:
    if file.endswith((".mp3", ".m4a")):
    full_path = os.path.join(root, file)

    parts = full_path.split(os.sep)
    try:
    artist = parts[2]
    album = parts[3]
    except IndexError:
    artist = "Unknown Artist"
    album = "Yandex.Music"

    filename = os.path.splitext(os.path.basename(file))[0]
    title = re.sub(r'^\d+\s*[-–—]\s*', '', filename).strip()

    tracks.append({
    'mp3': full_path,
    'cover': cover_path,
    'artist': artist,
    'title': title,
    'album': album
    })

    return tracks

    def tag_and_convert_audio(file_path: str, artist: str, title: str, album: str, cover_path: str = None):
    if file_path.endswith(".m4a"):
    mp3_path = file_path.replace(".m4a", ".mp3")
    subprocess.run([
    "ffmpeg", "-y", "-i", file_path,
    "-vn", "-ar", "44100", "-ac", "2", "-b:a", "192k",
    mp3_path
    ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    os.remove(file_path)
    file_path = mp3_path

    audio = EasyID3(file_path)
    audio['artist'] = artist
    audio['title'] = title
    audio['album'] = album
    audio.save()

    if cover_path:
    audio = ID3(file_path)
    with open(cover_path, 'rb') as img:
    audio['APIC'] = APIC(
    encoding=3,
    mime='image/jpeg',
    type=3,
    desc='Cover',
    data=img.read()
    )
    audio.save()

    return file_path

    @dp.message()
    async def handle_message(message: types.Message):
    if 'soundcloud.com' in message.text:
    await message.reply(" Загружаю с SoundCloud...")
    try:
    tracks = download_soundcloud(message.text)
    for track in tracks:
    final_path = tag_and_convert_audio(track['mp3'], track['artist'], track['title'], track['album'], track['cover'])
    dest = os.path.join(NAVIDROME_MUSIC_PATH, os.path.basename(final_path))
    copyfile(final_path, dest)
    await bot.send_audio(
    chat_id=message.chat.id,
    audio=FSInputFile(final_path),
    title=track['title'],
    performer=track['artist'],
    thumbnail=FSInputFile(track['cover']) if track['cover'] else None
    )
    except Exception as e:
    await message.reply(f" Ошибка: {e}")
    finally:
    clean_downloads()

    elif 'music.yandex.ru' in message.text:
    await message.reply(" Загружаю с Яндекс Музыки...")
    try:
    tracks = download_yandex_music(message.text)
    for track in tracks:
    final_path = tag_and_convert_audio(track['mp3'], track['artist'], track['title'], track['album'], track['cover'])
    dest = os.path.join(NAVIDROME_MUSIC_PATH, os.path.basename(final_path))
    copyfile(final_path, dest)
    await bot.send_audio(
    chat_id=message.chat.id,
    audio=FSInputFile(final_path),
    title=track['title'],
    performer=track['artist']
    )
    except Exception as e:
    await message.reply(f" Ошибка: {e}")
    finally:
    clean_downloads()

    else:
    await message.reply("Отправь ссылку на SoundCloud или Яндекс Музыку")
    asyncio.run(dp.start_polling(bot))
    После заполнения всех данных вам нужно установить нужные пакеты
    Код
    pip install aiogram requests yt-dlp mutagen
    sudo apt install ffmpeg
    pip install -U https://github.com/llistochek/yandex-music-downloader/archive/main.zip
    Для получения токена воспользуйтесь этой документацией
    Чтобы ваш скрипт всегда работал, нужно сделать systemd сервис, который будет запускаться вместе с запуском системы и работать бесперебойно
    Код
    sudo nano /etc/systemd/system/navidrome-bot.service

    Туда вставьте
    Код
    [Unit]
    Description=Navidrome Music Downloader Bot
    After=network.target

    [Service]
    ExecStart=/usr/bin/python3 /root/scripts/navidrome_music_downloader/bot.py
    WorkingDirectory=/root/scripts/navidrome_music_downloader
    Restart=always
    User=root
    Environment=PYTHONUNBUFFERED=1

    [Install]
    WantedBy=multi-user.target
    Дальше надо перезагрузить все daemon
    Код
    sudo systemctl daemon-reexec
    sudo systemctl daemon-reload
    И запустить наш сервис
    [CODE=code]sudo systemctl start navidrome-bot
    sudo systemctl enable navidrome-bot[/CODE]Вуа-ля! Ваш личный музыкальный сервис готов!

    4. Клиенты для разных устройств

    Android:
    1. Symfonium
    iOS
    1. substreamer
    2. iSub

    Вот все клиенты, которые я сам лично прочекал. На компе слушаю прям с браузера :kakashka:

    ===================================

    Спасибо за прочтение статьи!
    Надеюсь данный материал будет полезен именно гость
     
    Этот материал оказался полезным?
    Вы можете отблагодарить автора темы путем перевода средств на баланс
    Отблагодарить автора
    27 июн 2025 Изменено
  2. aliceskyte
    aliceskyte 27 июн 2025 куплю пива дам в рот, 200м от вас 134 8 окт 2021
    а почему бы просто не использовать ютуб музыку? ну или там ск тот же..
    зачем?
     
  3. ЯБылНоКем
    ЯБылНоКем 27 июн 2025 готовьте трон для короля:smile_beach:
    кому не плевать на саундклауд реперов ?
     
    1. Нинтендо
      ЯБылНоКем,
      между нами ЪЭ ЪЭ √
      между нами ЪЭ ЪЭ √
      между нами ЪЭ ЪЭ √
      вот такая вот такая ЪЭ ЪЭ √
    2. бинауральный
  4. рикка
    рикка 27 июн 2025 丰 Мне нехуй делать — я ем круассан 6085 4 апр 2019
    Да вроде ск и так без рекламы работает
     
    1. Посмотреть предыдущие комментарии (2)
    2. рикка
      json, хз тогда. С ру айпишника все треки без рекламы работают :smile_idk:
    3. json Автор темы
      рикка, да бля тема ваще не про рекламу на ск вы че дауны?
    4. рикка
      json, ну а для чего ещё мне всё это надо делать если не слушать музыку без рекламы
  5. aliceskyte
    aliceskyte 27 июн 2025 куплю пива дам в рот, 200м от вас 134 8 окт 2021
    бля парни покупайте себе вместо блядского яблока элитных зеленых роботов и пиратьте всё что душе угодно
     
  6. ТРАПИК
    ТРАПИК 27 июн 2025 悲しくなくないけど泣いてます なんだか、退屈なので泣いてます。 宇宙が溺れる零し みなさん逃げ場も有りません 11 929 12 май 2023
    а нахуй оно надо? если в ск нет рекламы
     
    1. Посмотреть предыдущие комментарии (4)
    2. json Автор темы
    3. ТРАПИК
      aliceskyte, я вчера слушал на саундклауде и не было
    4. KOKURO
      aliceskyte, только с ***, в ру нет рекласы
  7. MALWARE
    MALWARE 27 июн 2025 мяукаю 12 003 12 мар 2021
    круто конечно, но к сожалению там не будет рекомендаций

    да и гораздо проще настроить AIMP с облачным хранилищем (если self-hosted то nextcloud)
    --- Сообщение объединено с предыдущим 27 июн 2025
    а посмотри про funkwhale, как тебе оно?
     
    1. json Автор темы
      MALWARE, да кстати funkwhale крутая темка, но бля. мне моя тема больше нравится тк один докер и все готово
  8. Neptvne
    Neptvne 30 июн 2025 Данный пользователь не в зоне действия сети 613 19 июн 2019
    извини но круче zaycev. net ничего не будет....
     
    30 июн 2025 Изменено
  9. Harizeas
    Harizeas 30 июн 2025 €€€ 2492 10 мар 2021
    Было бы заебись если бы еще оттуда можно было треки в дискорд ботов грузить. Просто ссылкой в очередь в условного Rhytm
     
Загрузка...
Top