Загрузка...

Navidrome. We make personal SoundCloud for local use

Thread in Articles created by json Jun 27, 2025. (bumped Jun 29, 2025) 582 views

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

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


    1. Подготовка сервера
    Вам для этого понадобится сервер Ubuntu/Debilian. Требуется установить Docker при помощи этой команды
    Code
    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, который мы установили. Теперь можем создать отдельную аудиторию
    Code
    mkdir navidrome

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

    В этот файл мы вставим наш docker-compose конфиг, при помощи которого Navidrome сразу запуститься и будет работать на сервере в отдельном контейнере
    Code
    # 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
    Дальше для запуска нашего контейнера надо написать команду
    Code
    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))
    После заполнения всех данных вам нужно установить нужные пакеты
    Code
    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 сервис, который будет запускаться вместе с запуском системы и работать бесперебойно
    Code
    sudo nano /etc/systemd/system/navidrome-bot.service

    Туда вставьте
    Code
    [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
    Code
    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:

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

    Спасибо за прочтение статьи!
    Надеюсь данный материал будет полезен именно guest
     
    This article was useful for you?
    You can thank the author of the topic by transferring funds to your balance
    Thank the author
  2. aliceskyte
    aliceskyte Jun 27, 2025 куплю пива дам в рот, 200м от вас 134 Oct 8, 2021
    а почему бы просто не использовать ютуб музыку? ну или там ск тот же..
    зачем?
     
  3. ЯБылНоКем
    ЯБылНоКем Jun 27, 2025 готовьте трон для короля:smile_beach:
    кому не плевать на саундклауд реперов ?
     
    1. Нинтендо
      ЯБылНоКем,
      между нами ЪЭ ЪЭ √
      между нами ЪЭ ЪЭ √
      между нами ЪЭ ЪЭ √
      вот такая вот такая ЪЭ ЪЭ √
    2. бинауральный
  4. рикка
    рикка Jun 27, 2025 丰 Мне нехуй делать — я ем круассан 6085 Apr 4, 2019
    Да вроде ск и так без рекламы работает
     
    1. View previous comments (2)
    2. рикка
      json, хз тогда. С ру айпишника все треки без рекламы работают :smile_idk:
    3. json Topic starter
      рикка, да бля тема ваще не про рекламу на ск вы че дауны?
    4. рикка
      json, ну а для чего ещё мне всё это надо делать если не слушать музыку без рекламы
  5. aliceskyte
    aliceskyte Jun 27, 2025 куплю пива дам в рот, 200м от вас 134 Oct 8, 2021
    бля парни покупайте себе вместо блядского яблока элитных зеленых роботов и пиратьте всё что душе угодно
     
  6. ТРАПИК
    ТРАПИК Jun 27, 2025 悲しくなくないけど泣いてます なんだか、退屈なので泣いてます。 宇宙が溺れる零し みなさん逃げ場も有りません 11,946 May 12, 2023
    а нахуй оно надо? если в ск нет рекламы
     
    1. View previous comments (4)
    2. json Topic starter
    3. ТРАПИК
      aliceskyte, я вчера слушал на саундклауде и не было
    4. KOKURO
      aliceskyte, только с ***, в ру нет рекласы
  7. MALWARE
    MALWARE Jun 27, 2025 мяукаю 12,006 Mar 12, 2021
    круто конечно, но к сожалению там не будет рекомендаций

    да и гораздо проще настроить AIMP с облачным хранилищем (если self-hosted то nextcloud)
    The post was merged to previous Jun 27, 2025
    а посмотри про funkwhale, как тебе оно?
     
    1. json Topic starter
      MALWARE, да кстати funkwhale крутая темка, но бля. мне моя тема больше нравится тк один докер и все готово
  8. Neptvne
    Neptvne Jun 30, 2025 Данный пользователь не в зоне действия сети 615 Jun 19, 2019
    извини но круче zaycev. net ничего не будет....
     
  9. Harizeas
    Harizeas Jun 30, 2025 €€€ 2493 Mar 10, 2021
    Было бы заебись если бы еще оттуда можно было треки в дискорд ботов грузить. Просто ссылкой в очередь в условного Rhytm
     
Loading...
Top