трансфер с гитхаба

This commit is contained in:
2026-05-15 04:03:13 +06:00
parent e413b50f89
commit a6514cc14f
9 changed files with 768 additions and 21 deletions
+49 -7
View File
@@ -1,7 +1,6 @@
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*.py[codz]
*$py.class
# C extensions
@@ -47,7 +46,7 @@ htmlcov/
nosetests.xml
coverage.xml
*.cover
*.py,cover
*.py.cover
.hypothesis/
.pytest_cache/
cover/
@@ -107,17 +106,24 @@ ipython_config.py
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
#poetry.toml
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
#pdm.toml
.pdm-python
.pdm-build/
# pixi
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
#pixi.lock
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
# in the .venv directory. It is recommended not to include this directory in version control.
.pixi
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
@@ -130,6 +136,7 @@ celerybeat.pid
# Environments
.env
.envrc
.venv
env/
venv/
@@ -168,9 +175,44 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Cursor
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
# refer to https://docs.cursor.com/context/ignore-files
.cursorignore
.cursorindexingignore
# Marimo
marimo/_static/
marimo/_lsp/
__marimo__/
venv/
*.txt
*.mp3
downloads/
temp/
*.png
*.jpg
*.webp
test.py
*.json
+15 -12
View File
@@ -2,17 +2,20 @@ MIT License
Copyright (c) 2026 s13bby
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+205 -1
View File
@@ -1,3 +1,207 @@
# music_loader
# 🎵 Music Loader
Загрузчик музыки из онлайн-источников с поддержкой интерактивного поиска, пакетной загрузки и импорта плейлистов.
## 📋 Возможности
- 🔍 **Интерактивный поиск** — выбор трека из списка найденных результатов
- 📜 **Пакетная загрузка** — скачивание треков из плейлиста (файл `.txt`)
- 🌐 **Несколько источников** — поддержка hitmo и youtube
- 🎨 **Обложки треков** — автоматическое добавление обложек из Яндекс Музыки и iTunes
- 📥 **Импорт плейлистов** — загрузка списков треков из плейлистов Яндекс Музыки и Spotify
- 📀 **Поиск альбомов** — поиск и экспорт альбомов в плейлист из iTunes и Яндекс Музыки
- ⏱️ **Автоматическая задержка** — пауза между загрузками для избежания блокировок
- 📁 **Гибкое хранение** — настройка папки для сохранения файлов
## 🚀 Требования
- Python 3.13+
- Библиотеки:
```bash
pip install requests beautifulsoup4 tqdm mutagen pillow yandex_music yt_dlp
```
## 📖 Использование
### Базовый синтаксис
```bash
python main.py [опции]
```
### Опции
| Опция | Описание |
|-------|----------|
| `-h`, `--help` | Показать справку |
| `-a`, `--album` | Импортировать альбом (itunes, yandex) |
| `-i`, `--import_playlist` | Импортировать плейлист (yandex, spotify) |
| `-p`, `--playlist` | Путь к файлу плейлиста (`.txt`) |
| `-s`, `--source` | Источник: `hitmo`, `youtube` (по умолчанию: `hitmo`) |
### Примеры
**Интерактивный режим** (поиск и выбор трека):
```bash
python main.py
python main.py -s youtube
```
**Пакетная загрузка** (из плейлиста):
```bash
python main.py -p playlist.txt
python main.py -s youtube -p playlist.txt
```
**Импорт плейлиста**:
```bash
python main.py -i yandex
python main.py -i spotify
```
**Поиск альбома**:
```bash
python main.py -a
```
## 🎵 Импорт плейлиста из Яндекс Музыки
Для импорта плейлиста из Яндекс Музыки:
1. Откройте нужный плейлист на [music.yandex.ru](https://music.yandex.ru)
2. Нажмите на **три точки** (⋮) в верхней части плейлиста
3. Выберите **«Поделиться»** → **«HTML-код»**
4. Скопируйте ссылку из открывшегося окна
5. Запустите:
```bash
python main.py -i yandex
```
6. Вставьте скопированный HTML-код
После этого в текущей папке появится файл `<название_плейлиста>.txt` с треками.
## 🎵 Импорт плейлиста из Spotify
Для импорта плейлиста из Spotify:
1. Открой https://open.spotify.com/ в браузере
2. Нажми **F12** (откроются DevTools)
3. Перейди во вкладку **Network**
4. Останови запись (кнопка ⏹️) и очисти историю (кнопка 🚫)
5. **Перед открытием плейлиста** нажми запись (кнопка ⏺️)
6. Перейди на интересующий плейлист
7. Дождись полной загрузки и отключи запись
8. Найди элементы с названием **query** в начале списка (или прокликай первые 10–15 запросов)
9. Нажми на один из элементов **query**
10. Перейди во вкладку **Response** — если видишь длинный JSON, это нужный запрос
11. ПКМ по элементу → **Copy** → **Copy response**
12. Создай файл с расширением `.json` и сохрани в него скопированный ответ
13. Запусти:
```bash
python main.py -i spotify
```
14. Укажи путь к созданному JSON-файлу
После этого в текущей папке появится файл `<случайное_имя>.txt` с треками.
## 📀 Поиск и импорт альбома
Для поиска и импорта альбома:
1. Запустите:
```bash
python main.py -a
```
2. Введите артиста и название альбома в формате: `артист название`
3. Альбом будет найден в iTunes или Яндекс Музыке
4. В текущей папке появится файл `<название_альбома>.txt` с треками
## 📝 Формат плейлиста
Создайте текстовый файл (например, `playlist.txt`) в формате:
```
артист название_трека
еще артист еще трек
booker джанк
```
Каждая строка — отдельный запрос на поиск и загрузку.
## ⏱️ Задержка между загрузками
При использовании плейлиста между скачиваниями треков автоматически добавляется задержка, указанная в `settings.json` (по умолчанию: **1 секунда**) для предотвращения временной блокировки со стороны сайта.
## ⚙️ Настройки
Создайте файл `settings.json` в корне проекта:
```json
{
"delay": 1,
"save to": "downloads/",
"cover": true,
"source": "hitmo",
"proxy": "http://ip:port"
}
```
| Параметр | Описание |
|----------|----------|
| `delay` | Задержка между запросами (сек) |
| `save to` | Папка для сохранения треков |
| `cover` | Автоматическая установка обложек |
| `source` | Источник по умолчанию (`hitmo` или `youtube`) |
| `proxy` | Прокси для YouTube (требуется для работы в РФ) |
## 🌐 Источники
| Источник | Описание |
|----------|----------|
| **hitmo** (ru, eu) | Основной источник |
| **youtube** | Вспомогательный источник (требуется прокси для работы в РФ) |
## 🎨 Обложки и метаданные
| Сервис | Обложки | Альбомы | Плейлисты |
|--------|---------|---------|-----------|
| Яндекс Музыка | ✅ | ✅ | ✅ |
| iTunes | ✅ | ✅ | ❌ |
| Spotify | ❌ | ❌ | ✅ |
## 📂 Структура проекта
```
music_loader/
├── main.py
├── core/
│ ├── album.py # Поиск альбома, экспортирование в плейлист
│ ├── cover.py # Поиск, скачивание и установка обложек
│ ├── hitmo.py # Парсинг сайта, поиск и скачивание треков
│ ├── import_playlist.py # Импорт плейлистов из Яндекс и Spotify
│ └── youtube.py # Поиск и скачивание треков с YouTube
├── settings.json
├── LICENSE
├── README.md
└── .gitignore
```
---
> ## Дисклеймер | Disclaimer
> Данный проект предназначен исключительно для личного и образовательного использования.
>
> Программа использует сторонние источники данных для поиска треков, обложек и метаданных. В отдельных случаях возможны неточности в результатах поиска, особенно при работе с плейлистами.
>
> Пользователь несет полную ответственность за соблюдение авторских прав и условий использования контента при загрузке или использовании материалов, полученных с помощью данного проекта.
>
> Автор проекта не связан с сервисами, используемыми программой, и не несет ответственности за возможные ограничения, блокировки или иные действия со стороны сторонних сервисов.
>
> This project is intended for personal and educational use only.
>
> The software uses third-party sources to search for tracks, artwork, and metadata. In some cases, inaccuracies may occur in search results, especially when processing playlists.
>
> Users are solely responsible for complying with copyright laws and the terms of service of the platforms when downloading or using content obtained through this project.
>
> The project author is not affiliated with the services used by the software and is not responsible for any restrictions, bans, or other actions imposed by third-party platforms.
>
> **сделано:** s13bby
+117
View File
@@ -0,0 +1,117 @@
import requests
import os
from yandex_music import Client
def get_itunes_search_data(query):
url = "https://itunes.apple.com/search"
params = {"term": query, "entity": "album", "limit": 1}
try:
response = requests.get(url, params=params)
return response.json()
except Exception as e:
print(f"iTunes API Error: {e}")
return None
def extract_itunes_album(data):
if not data or data.get("resultCount") == 0:
return None
return data["results"][0]
def get_itunes_tracks(album_id):
params = {"id": album_id, "entity": "song"}
response = requests.get("https://itunes.apple.com/lookup", params=params)
results = response.json().get("results", [])
return results[1:] if len(results) > 1 else []
def format_itunes_track(item, artist_name):
ms = item.get('trackTimeMillis', 0)
duration = f"{ms // 60000}:{(ms // 1000) % 60:02d}"
return {
"no": item.get("trackNumber"),
"artist": artist_name,
"title": item.get("trackName"),
"duration": duration
}
def get_yandex_client():
try:
return Client().init()
except:
return None
def find_yandex_album(client, query):
search = client.search(query, type_='album')
if not search.albums or not search.albums.results:
return None
return search.albums.results[0]
def format_yandex_track(track):
sec = track.duration_ms // 1000
duration = f"{sec // 60}:{sec % 60:02d}"
artists = ", ".join([a.name for a in track.artists])
return {
"no": getattr(track, 'number', 0),
"artist": artists,
"title": track.title,
"duration": duration
}
def process_itunes(query):
data = get_itunes_search_data(query)
album = extract_itunes_album(data)
if not album:
return None
raw_tracks = get_itunes_tracks(album["collectionId"])
artist = album.get("artistName", "Unknown")
return {
"title": album["collectionName"],
"year": album["releaseDate"][:4],
"tracks": [format_itunes_track(t, artist) for t in raw_tracks]
}
def process_yandex(query):
client = get_yandex_client()
if not client: return None
album_info = find_yandex_album(client, query)
if not album_info: return None
full_album = client.albums_with_tracks(album_info.id)
tracks = []
for vol in full_album.volumes:
for t in vol:
tracks.append(format_yandex_track(t))
return {
"title": album_info.title,
"year": album_info.year or "Unknown",
"tracks": tracks
}
def save_to_file(album_data):
filename = f"{album_data['title']}.txt"
with open(filename, "w", encoding="utf-8") as f:
for t in album_data['tracks']:
line = f"{t['no']}. {t['artist']} - {t['title']} ({t['duration']})"
print(line)
f.write(f"{t['artist']} {t['title']}\n")
print(f"\nГотово! Сохранено в {filename}")
def main(request):
print(f"Поиск '{request}' в iTunes...")
result = process_itunes(request)
if not result:
print("В iTunes не найдено. Ищем в Яндекс.Музыке...")
result = process_yandex(request)
if result:
print(f"Найдено: {result['title']} ({result['year']})")
save_to_file(result)
else:
print(f"Альбом '{request}' не найден ни в одном источнике.")
+75
View File
@@ -0,0 +1,75 @@
import io
import requests
import os
import time
import json
from yandex_music import Client
from PIL import Image
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC
def get_yandex_cover(query, settings):
try:
client = Client().init()
time.sleep(settings["delay"])
search_result = client.search(query, type_='track')
if search_result.tracks and search_result.tracks.results:
track_obj = search_result.tracks.results[0]
if track_obj.og_image:
return "https://" + track_obj.og_image.replace('%%', '1000x1000')
return None
except:
return None
def get_itunes_cover(query, settings):
url = "https://itunes.apple.com/search"
params = {"term": query, "entity": "song", "limit": 1}
try:
time.sleep(settings["delay"])
response = requests.get(url, params=params, timeout=5)
data = response.json()
if data.get("resultCount", 0) > 0:
return data["results"][0]["artworkUrl100"].replace("100x100bb.jpg", "1000x1000bb.jpg")
return None
except:
return None
def get_best_cover(query, settings):
cover_url = get_itunes_cover(query, settings)
if not cover_url:
cover_url = get_yandex_cover(query, settings)
return cover_url
def set_cover(track_path, request):
with open("settings.json", "r", encoding="utf-8") as file:
settings = json.load(file)
cover_url = get_best_cover(request, settings)
if not cover_url:
return
try:
response = requests.get(cover_url, timeout=10)
img = Image.open(io.BytesIO(response.content)).convert("RGB")
buffer = io.BytesIO()
img.save(buffer, format="JPEG", quality=90)
img_data = buffer.getvalue()
audio = MP3(track_path, ID3=ID3)
if audio.tags is None:
audio.add_tags()
audio.tags.add(
APIC(
encoding=3,
mime='image/jpeg',
type=3,
desc='Cover',
data=img_data
)
)
audio.save()
return True
except Exception as e:
print(f"Ошибка: {e}")
return False
+100
View File
@@ -0,0 +1,100 @@
import requests
import sys
import re
import os
import time
import json
from bs4 import BeautifulSoup
from tqdm import tqdm
def ping():
pages = ["https://rus.hitmotop.com/", "https://eu.hitmo-top.com/"]
for page in pages:
status = requests.get(page)
if status.status_code == 200:
return page
print("Ресурсы не доступны")
sys.exit()
def parse(request, page, settings):
url = page + "search?q=" + request.replace(" ", "%20")
time.sleep(settings["delay"])
content = BeautifulSoup(requests.get(url).text, "html.parser")
return content
def get_artists(content):
return [artists.get_text(strip=True) for artists in content.find_all("div", class_="track__desc")]
def get_titles(content):
return [titles.get_text(strip=True) for titles in content.find_all("div", class_="track__title")]
def get_links(content):
return [links.get("href") for links in content.find_all("a", class_="track__download-btn")]
def get_data_dict(content):
dictionary = {
i: [artist, title, link]
for i, (artist, title, link) in enumerate(zip(get_artists(content), get_titles(content), get_links(content)))
}
return dictionary
def get_url(index, dictionary):
return dictionary[index][2]
def handle():
selected_index = int(input("Скачать трек: "))
return selected_index
def playlist():
return 0
def get_filename(index, dictionary):
filename = f"{dictionary[index][0]} - {dictionary[index][1]}.mp3"
filename = re.sub(r'[*"?<>|:$&()\[\]!/]+', "", filename)
return filename
def get_path_to_file(filename, settings):
os.makedirs(settings["save to"], exist_ok=True)
path_to_file = os.path.join(settings["save to"], filename)
return path_to_file
def print_content(dictionary):
for i in range(len(dictionary)):
print(f"{i}. {dictionary[i][0]} - {dictionary[i][1]}")
return None
def download(url, path_to_file, filename, settings):
time.sleep(settings["delay"])
with requests.get(url, stream=True, headers={"User-Agent": "Mozilla/5.0"}) as r:
r.raise_for_status()
size = int(r.headers.get('content-length', 0))
with open(path_to_file, 'wb') as f, tqdm(total=size, unit='B', unit_scale=True) as pbar:
for chunk in r.iter_content(8192):
pbar.update(f.write(chunk))
print(f"Скачано: {filename}")
def main(request, mode):
try:
with open("settings.json", "r", encoding="utf-8") as file:
settings = json.load(file)
except Exception as e:
print(f"Ошибка открытия файла: {e}")
sys.exit()
page = ping()
content = parse(request, page, settings)
dictionary = get_data_dict(content)
if not dictionary:
print(f"По запросу {request} ничего не найдено.")
return
if mode == "handle":
print_content(dictionary)
index = handle()
else:
index = playlist()
url = get_url(index, dictionary)
filename = get_filename(index, dictionary)
path_to_file = get_path_to_file(filename, settings)
download(url, path_to_file, filename, settings)
return path_to_file
+71
View File
@@ -0,0 +1,71 @@
import json
import sys
import re
import random
from yandex_music import Client
def yandex(html_):
pattern = r"playlist/([\w\.-]+)/(\d+)"
match = re.search(pattern, html_)
if match:
owner = match.group(1)
kind = match.group(2)
else:
sys.exit()
client = Client().init()
try:
playlist = client.users_playlists(kind, owner)
print(f"\nПлейлист: {playlist.title}")
print(f"Всего треков: {playlist.track_count}")
print("-" * 30)
file = open(f"{playlist.title}.txt", "w", encoding="utf-8")
for item in playlist.tracks:
track = item.track
artists = ", ".join([a.name for a in track.artists])
title = track.title
file.write(f"{artists} {title}\n")
print(f"{artists}{title}")
print(f"\n{playlist.title}.txt плейлист сформирован")
file.close()
sys.exit()
except Exception:
print(f"Ошибка при чтении плейлиста: {Exception}")
sys.exit()
def spotify(playlist):
try:
with open(playlist, "r", encoding="utf-8") as f:
data = json.load(f)
except Exception as e:
print(f"Ошибка открытия файла: {e}")
sys.exit()
temp_name = str(random.randint(0, 2**32 -1))[:5]
items = data["data"]["playlistV2"]["content"]["items"]
file = open(f"{temp_name}.txt", "w", encoding="utf-8")
for item in items:
track = item["itemV2"]["data"]
title = track["name"]
artist = track["artists"]["items"][0]["profile"]["name"]
file.write(f"{artist} {title}\n")
print(f"{artist}{title}")
file.close()
print(f"\n{temp_name}.txt плейлист сформирован")
sys.exit()
+53
View File
@@ -0,0 +1,53 @@
import yt_dlp
import os
import json
def download_track(input_):
with open ("settings.json", "r", encoding="utf-8") as data:
settings = json.load(data)
query = f"ytsearch1:{input_}"
if not os.path.exists(settings["save to"]):
os.makedirs(settings["save to"])
ydl_opts = {
"format": "bestaudio/best",
"postprocessors": [{
"key": "FFmpegExtractAudio",
"preferredcodec": "mp3",
"preferredquality": "320",
}],
"proxy": settings["proxy"],
"extractor_args": {
"youtube": {
"player_client": ["android"],
"skip": ["webpage"],
}
},
"nocheckcertificate": True,
"outtmpl": settings["save to"] + "%(title)s.%(ext)s",
"quiet": False,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info(query, download=True)
if 'entries' in info_dict:
video_info = info_dict['entries'][0]
else:
video_info = info_dict
file_path = ydl.prepare_filename(video_info)
final_path = os.path.splitext(file_path)[0] + ".mp3"
print(f"Скачано: {final_path}")
return final_path
except Exception as e:
print(f"Ошибка при скачивании: {e}")
return False
+82
View File
@@ -0,0 +1,82 @@
import argparse
import sys
import json
import time
from core import album, cover, hitmo, import_playlist, youtube
try:
with open("settings.json", "r", encoding="utf-8") as data:
settings = json.load(data)
except Exception:
print("\nФайл с настройками settings.json не найден.\nВосстановите этот файл и перезапустите скрипт")
sys.exit()
def arguments():
parser = argparse.ArgumentParser(description="Загрузчик музыки из онлайн-источников с поддержкой интерактивного поиска, пакетной загрузки и импорта плейлистов.")
parser.add_argument("-a", "--album", action="store_true", help="Импортировать альбом")
parser.add_argument("-i", "--import_playlist", type=str, default="", help="Импортировать плейлист")
parser.add_argument("-p", "--playlist", type=str, default="", help="Скачать список из txt")
parser.add_argument("-s", "--source", type=str, default=f"{settings['source']}", help="Источник (hitmo/youtube)")
args = parser.parse_args()
return args.album, settings["cover"], args.import_playlist, args.playlist, args.source
def main():
arg_album, cfg_cover, arg_import, arg_playlist, arg_source = arguments()
if arg_album:
request = input("Введите артиста и название альбома (синтаксис: артист название)\n")
album.main(request)
sys.exit()
elif arg_import == "yandex":
request = input("Введите HTML-код плейлиста\n")
import_playlist.yandex(request)
sys.exit()
elif arg_import == "spotify":
request = input("Введите путь до плейлиста в формате json\n")
import_playlist.spotify(request)
sys.exit()
elif arg_playlist:
try:
with open(arg_playlist, "r", encoding="utf-8") as f:
lines = [l.strip() for l in f.readlines() if l.strip()]
except Exception as e:
print(f"Ошибка открытия файла: {e}")
sys.exit()
for i, line in enumerate(lines, 1):
if arg_source == "hitmo":
result = hitmo.main(line, "playlist")
else:
result = youtube.download_track(line)
if result:
cover.set_cover(result, line)
print(f"[{i}/{len(lines)}] Обработан: {line}")
else:
print(f"Трек {line} не найден, пропускаем")
if i < len(lines):
time.sleep(settings["delay"])
sys.exit()
elif arg_source == "youtube":
request = input("Введите артиста и название трека\n")
result = youtube.download_track(request)
if result:
cover.set_cover(result, request)
else:
print(f"Трек {request} не найден")
sys.exit()
else:
request = input("Введите артиста и название трека\n")
result = hitmo.main(request, "handle")
if result:
cover.set_cover(result, request)
sys.exit()
if __name__ == "__main__":
main()