Skip to content

Commit

Permalink
First prototype is kinda working
Browse files Browse the repository at this point in the history
  • Loading branch information
BredeFK committed May 29, 2023
1 parent f177a60 commit 34c89ee
Show file tree
Hide file tree
Showing 18 changed files with 217 additions and 37 deletions.
4 changes: 3 additions & 1 deletion SoulseekAPI/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ bld/
[Oo]ut/
msbuild.log
msbuild.err
msbuild.wrn
msbuild.wrn

Songs/
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added SoulseekAPI/.vs/SoulseekAPI/v17/.futdcache.v2
Binary file not shown.
2 changes: 1 addition & 1 deletion SoulseekAPI/SoulseekAPI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33414.496
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoulseekAPI", "SoulseekAPI\SoulseekAPI.csproj", "{0196C397-2A6C-4C21-B662-CCFDF27F26AE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SoulseekAPI", "SoulseekAPI\SoulseekAPI.csproj", "{0196C397-2A6C-4C21-B662-CCFDF27F26AE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
12 changes: 6 additions & 6 deletions SoulseekAPI/SoulseekAPI/Controllers/SoulseekSongsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ SoulSeekService soulSeekService
}

[HttpGet(Name = "GetSoulseekSongs")]
public async Task<List<Track>> Get(string songName, string? filterType, int? minSize, int? maxSize)
public async Task<List<Track>> Get(string song, string artist, string? filterType, string exclude, long? minSize, long? maxSize)
{
var list = await _soulSeekService.GetTracks(songName, filterType, minSize, maxSize);

_logger.LogInformation("GET " + song + " by " + artist);
var list = await _soulSeekService.GetTracks(song, artist, filterType, exclude, minSize, maxSize);
return list;
}

[HttpPost(Name = "DownloadSoulseekSong")]
public async Task Post(Track track)
public async Task Post(Track track, string folder)
{
await _soulSeekService.DownloadTrack(track);

_logger.LogInformation("POST " + track.Name + " to folder " + folder);
await _soulSeekService.DownloadTrack(track, folder);
}
}
}
3 changes: 3 additions & 0 deletions SoulseekAPI/SoulseekAPI/Models/Track.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ public class Track
public string OriginalName { get; set; }
public string Type { get; set; }
public long Size { get; set; }
public long UploadSpeed { get; set; }
public int QueueLength { get; set; }
public bool HasFreeUploadSlot { get; set; }
}
}
51 changes: 41 additions & 10 deletions SoulseekAPI/SoulseekAPI/Services/SoulSeekService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,19 @@ private async Task Connect()
}
}

public async Task<List<Track>> GetTracks(string songName, string? filterType, int? minSize, int? maxSize)
public async Task<List<Track>> GetTracks(string songName, string artist, string? filterType, string exclude, long? minSize, long? maxSize)
{
await Connect();
var query = SearchQuery.FromText(songName);

var excludeList = exclude.Split(',').ToList();
excludeList.RemoveAt(excludeList.Count - 1);

var query = SearchQuery.FromText(songName + " " + artist);
var searchResults = await _soulseekClient.SearchAsync(query);
var tracks = GetTrackList(searchResults.Responses);
var tracks = GetTrackList(searchResults.Responses, songName, excludeList);

var list = tracks;


if (filterType != null)
{
list = list.Where(track => track.Type == filterType).ToList();
Expand All @@ -63,20 +66,29 @@ public async Task<List<Track>> GetTracks(string songName, string? filterType, in
list = tracks;
}

list = list.OrderByDescending(x => x.Size).Take(10).ToList();
list = list.OrderBy(x => x.HasFreeUploadSlot).ThenByDescending(x => x.Size).ThenBy(x => x.UploadSpeed).Take(10).ToList();


return list;
}

public async Task DownloadTrack(Track track)
public async Task DownloadTrack(Track track, string folder)
{
await Connect();
await _soulseekClient.DownloadAsync(track.Username, track.OriginalName, track.Name);
var path = "Songs\\" +folder + "\\" + track.Name;

try
{
await _soulseekClient.DownloadAsync(track.Username, track.OriginalName, path);
}
catch (Exception ex)
{
_logger.LogError(ex.ToString(), ex);
}

}

private List<Track> GetTrackList(IReadOnlyCollection<SearchResponse> response)
private List<Track> GetTrackList(IReadOnlyCollection<SearchResponse> response, string songName, List<string> excludeList)
{
var list = new List<Track>();
foreach (var result in response)
Expand All @@ -89,10 +101,29 @@ private List<Track> GetTrackList(IReadOnlyCollection<SearchResponse> response)
Name = file.Filename.Split('\\').Last(),
OriginalName = file.Filename,
Type = file.Filename.Split('.').Last(),
Size = file.Size
Size = file.Size,
QueueLength = result.QueueLength,
UploadSpeed = result.UploadSpeed,
HasFreeUploadSlot = result.HasFreeUploadSlot
};

list.Add(track);
var trackName = track.Name.ToUpper();
if (trackName.Contains(songName.ToUpper()) && track.QueueLength <= 5)
{
var containsName = false;
foreach (var excludeName in excludeList)
{
if (trackName.Contains(excludeName.ToUpper()))
{
containsName = true;
}
}

if (!containsName)
{
list.Add(track);
}
}
}
}
return list;
Expand Down
94 changes: 75 additions & 19 deletions Spotify/getPlaylistSongs.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
import codecs
import json
import os.path

import time
import requests
from dotenv import dotenv_values
import urllib3
import util.colors as colors
import util.printWithColors as printColor
import util.functions as functions
import soulseekService

from dotenv import dotenv_values

BASE_URL = "https://api.spotify.com/v1"
BASE_URL_ACCESS_TOKEN = "https://accounts.spotify.com/api/token"


def main():
printColor.blue("Starting...")
config = dotenv_values(".env")
token = get_token(config)
if token != 0:
response = make_request_to_spotify(f'/playlists/{config["PLAYLIST_ID"]}', token)
if response != "":
prettify_tracks(response)
urllib3.disable_warnings()
ans = input("Do you want to use songs from .json file (y/n)?:")
if ans.upper() == 'Y':
file_name = input("Enter complete file name:")
read_tracks_from_file(file_name)
else:
printColor.red("Could not get token")
start = time.time()
config = dotenv_values(".env")
token = get_token(config)
if token != 0:
response = make_request_to_spotify(f'/playlists/{config["PLAYLIST_ID"]}', token)
if response != "":
process_tracks(response)
end = time.time()
functions.finished_print(end - start)
else:
printColor.red("Could not get token")


def init_access_token(client_credentials, client_id, client_secret):
Expand Down Expand Up @@ -58,15 +71,17 @@ def make_request_to_spotify(url_suffix, token):
req = requests.get(url=f'{BASE_URL}{url_suffix}?market=NO', headers=headers)
response = req.json()
if req.status_code == 200:
printColor.green("Success!")
print(
f'Found playlist on Spotify called {colors.GREEN}{response["name"]}{colors.ENDC} '
f'containing {colors.GREEN}{len(response["tracks"]["items"])}{colors.ENDC} songs')
else:
printColor.red(f'Error getting body {req.status_code}: {response}')
return ""

return response


def prettify_tracks(json_response):
def process_tracks(json_response):
tracks = json_response["tracks"]
if tracks["total"] == tracks["limit"]:
printColor.red("Response may have been limited!")
Expand All @@ -77,9 +92,13 @@ def prettify_tracks(json_response):
items = tracks["items"]
if not os.path.exists("./playlists"):
os.mkdir("./playlists")
playlist_file = codecs.open(f'playlists/{playlist_name}_{owner}.txt', "w", "utf-8")

playlist_name_prefix = f'{playlist_name}_{functions.get_date_and_time()}'
playlist_file = codecs.open(f'playlists/{playlist_name_prefix}.txt', "w", "utf-8")
print()
for track in items:
soulseek_songs = []
no_result_songs = []
for index, track in enumerate(items):
track_object = track["track"]
track_name = track_object["name"]
artists = track_object["artists"]
Expand All @@ -90,14 +109,51 @@ def prettify_tracks(json_response):
separator = ", "
all_artists = separator.join(artist_names)

print(f'{colors.GREEN}{track_name} {colors.BLUE}{all_artists}{colors.ENDC}')
playlist_file.write(f'{track_name} - {all_artists}\n')
song = soulseekService.search_for_song((index + 1), all_artists, track_name, "flac", "edit,", 100000, 0)
if song is not None:
soulseek_songs.append(song)
else:
no_result_songs.append(f'{track_name} by {all_artists}')

playlist_file.close()
print(
f'\n\nFound {colors.GREEN}{tracks["total"]}{colors.ENDC} tracks in the playlist '
f'{colors.GREEN}{playlist_name}{colors.ENDC} made by {colors.BLUE}{owner}{colors.ENDC} on {colors.GREEN}Spotify{colors.ENDC}')
print(f'Playlist tracks can be found in "{playlist_file.name}"')

playlist_json = codecs.open(f'playlists/{playlist_name_prefix}.json', "a", "utf-8")
json.dump(soulseek_songs, playlist_json)
playlist_json.close()

functions.print_spotify_details(tracks["total"], playlist_name, owner, playlist_file.name, playlist_json.name)

if len(soulseek_songs) > 0:
printColor.green(
f'Found {len(soulseek_songs)} of {tracks["total"]} songs on Soulseek, starting download now...')
download_songs(soulseek_songs, playlist_name)
else:
printColor.red(f'Could not find any songs on Soulseek :(')
for song in no_result_songs:
printColor.red(f'Could not find {song}')


def read_tracks_from_file(file_name):
path = f'./playlists/{file_name}'
if os.path.exists(path) and file_name.endswith(".json"):
printColor.green(f'Parsing file "{file_name}" now...')
file = open(path, 'r')
songs = json.load(file)
playlist_name = file_name.split("_")[0]
start = time.time()
download_songs(songs, playlist_name)
functions.finished_print(time.time() - start)
else:
printColor.red(f'Could not find a file named "{file_name}"')


def download_songs(songs, playlist_name):
folder_name = f'{playlist_name}_{functions.get_date_and_time()}'
if not os.path.exists(f'../SoulseekAPI/SoulseekAPI/Songs{folder_name}'):
os.mkdir(f'../SoulseekAPI/SoulseekAPI/Songs/{folder_name}')
for song in songs:
soulseekService.download_song(song, folder_name)


main()
67 changes: 67 additions & 0 deletions Spotify/soulseekService.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import time
import requests
from requests import ConnectTimeout

import util.printWithColors as printColor
import util.colors as colors

BASE_URL_API = "https://localhost:7069"
TIMEOUT_IN_SECONDS = (5 * 60) # 5 Minutes


def search_for_song(index, artist, song, filter_type, exclude, min_size, max_size):
printColor.blue(f'{index}: Searching for {song} by {artist}...')
headers = {
"Accept": 'Application/json'
}

try:
start = time.time()
req = requests.get(
url=f'{BASE_URL_API}/SoulseekSongs'
f'?song={song}&artist={artist}&filterType={filter_type}&exclude={exclude}&minSize={min_size}',
headers=headers,
verify=False,
timeout=TIMEOUT_IN_SECONDS
)
end = time.time()
if req.status_code == 200:
response = req.json()
if len(response) != 0:
printColor.green(f'Success: Found {len(response)} songs in {round(end - start, 2)} seconds')
return response[0]
else:
printColor.red(f'Could not find {song} by {artist}')
return None
else:
printColor.red(f'Could not find any results for {song} by {artist}, got {req.status_code}')
return None
except ConnectTimeout:
printColor.red(f'Could not find any results for {song} by {artist}, ConnectTimeout')
return None
except Exception:
printColor.red("Something went wrong with searching for songs")


def download_song(song, folder_name):
print(f'Downloading {colors.GREEN}{song["name"]}{colors.ENDC}')

try:
start = time.time()
req = requests.post(
url=f'{BASE_URL_API}/SoulseekSongs?folder={folder_name}',
json=song,
verify=False,
timeout=TIMEOUT_IN_SECONDS
)
end = time.time()

if req.status_code == 200:
printColor.green(f'Success: Downloaded {song["name"]} in {round(end - start, 2)} seconds')
else:
printColor.red(
f'Could not download {song["name"]} in {round(end - start, 2)} seconds, got {req.status_code}')
except ConnectTimeout:
printColor.red(f'Could not download song {song["name"]}, ConnectTimeout')
except Exception:
printColor.red(f'Something went wrong with downloading song {song["name"]}')
21 changes: 21 additions & 0 deletions util/functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from datetime import datetime
import util.colors as colors
import util.printWithColors as printColor


def get_date_and_time():
return datetime.now().strftime("%d%m%Y_%H.%M.%S")


def finished_print(diff):
printColor.blue(f'Finished! Total time: {round(diff, 2)} s. ({round(diff / 60, 2)} min.)')


def print_spotify_details(total_tracks, playlist_name, owner, playlist_txt_file, playlist_json_file):
print(
f'\n\nFound {colors.GREEN}{total_tracks}{colors.ENDC} tracks in the playlist '
f'{colors.GREEN}{playlist_name}{colors.ENDC} made by '
f'{colors.BLUE}{owner}{colors.ENDC} on {colors.GREEN}Spotify{colors.ENDC}\n'
f'Playlist tracks in text format can be found in {colors.BLUE}{playlist_txt_file}{colors.ENDC}\n'
f'Playlist tracks in JSON format can be found in {colors.BLUE}{playlist_json_file}{colors.ENDC}\n'
)

0 comments on commit 34c89ee

Please sign in to comment.