Skip to content

Commit

Permalink
Release v4.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
xnetcat authored Nov 8, 2022
2 parents eec303e + 4445b88 commit a034397
Show file tree
Hide file tree
Showing 16 changed files with 14,635 additions and 7,111 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/docker-hub-image-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,22 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}

- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
platforms: linux/arm/v7,linux/arm64,linux/amd64
push: true
tags: spotdl/spotify-downloader:latest
1 change: 1 addition & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ disable=
R0903, # too-few-public-methods
R0914, # too-many-locals
W0703, # broad-except
W0640, # cell-var-from-loop
214 changes: 107 additions & 107 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "spotdl"
version = "4.0.2"
version = "4.0.3"
description = "Download your Spotify playlists and songs along with album art and metadata"
license = "MIT"
authors = ["spotDL Team <spotdladmins@googlegroups.com>"]
Expand Down
9 changes: 1 addition & 8 deletions spotdl/console/entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from spotdl.download import Downloader
from spotdl.providers.audio.base import AudioProviderError
from spotdl.providers.audio.ytmusic import YouTubeMusic
from spotdl.utils.config import DEFAULT_CONFIG, ConfigError, get_config, get_config_file
from spotdl.utils.config import DEFAULT_CONFIG, get_config, get_config_file
from spotdl.utils.arguments import parse_arguments
from spotdl.utils.spotify import SpotifyClient, SpotifyError
from spotdl.utils.console import ACTIONS
Expand Down Expand Up @@ -69,13 +69,6 @@ def entry_point():
if is_ffmpeg_installed() is False:
download_ffmpeg()

try:
get_config()
except ConfigError:
config_path = get_config_file()
with open(config_path, "w", encoding="utf-8") as config_file:
json.dump(DEFAULT_CONFIG, config_file, indent=4)

# Check if sys.argv contains an action
# If it does, we run the action and exit
for action_name, action in ACTIONS.items():
Expand Down
2 changes: 1 addition & 1 deletion spotdl/download/progress_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def set_song_count(self, count: int) -> None:
self.overall_task_id = self.rich_progress_bar.add_task(
description="Total",
message=(
f"{self.overall_completed_tasks}/{int(self.overall_total / 100)}"
f"{self.overall_completed_tasks}/{int(self.overall_total / 100)} "
"complete"
),
total=self.overall_total,
Expand Down
152 changes: 129 additions & 23 deletions spotdl/providers/audio/youtube.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,15 @@ def search(self, song: Song) -> Optional[str]:
isrc_results = self.get_results(song.isrc)

if isrc_results and len(isrc_results) == 1:
isrc_result = isrc_results[0]
isrc_result = self.order_results(isrc_results, song)
if len(isrc_result) == 1:
isrc_link, isrc_score = isrc_result.popitem()

if isrc_result and isrc_result.watch_url is not None:
return isrc_result.watch_url
if isrc_score > 90:
# print(f"# RETURN URL - {isrc_link} - isrc score")
return isrc_link

# print(f"No results for ISRC: {song.isrc}")

search_query = create_song_title(song.name, song.artists).lower()

Expand All @@ -55,10 +60,10 @@ def search(self, song: Song) -> Optional[str]:
return None

if self.filter_results:
ordered_results = {results[0].watch_url: 100}
else:
# Order results
ordered_results = self.order_results(results, song)
else:
ordered_results = {results[0].watch_url: 100}

# No matches found
if len(ordered_results) == 0:
Expand All @@ -69,8 +74,36 @@ def search(self, song: Song) -> Optional[str]:
# Sort results by highest score
sorted_results = sorted(result_items, key=lambda x: x[1], reverse=True)

# Return the first result
return sorted_results[0][0]
last_simlar_index = 1
best_score, _ = sorted_results[0][1]

# Get few results with score close to the best score
for index, (_, (score, _)) in enumerate(sorted_results):
if (best_score - score) > 8:
last_simlar_index = index
break

# Get the best results from the similar results
best_results = sorted_results[:last_simlar_index]

# If we have only one result, return it
if len(best_results) == 1:
# print(f"# RETURN URL - {sorted_results[0][0]} - sorted, no best results")
return sorted_results[0][0]

# print(f"# best results: {best_results}")

# If we have more than one result,
# return the one with the highest score
# and most views
views_data = [best_result[1][1] for best_result in best_results]

# print(f"# views_data: {views_data}")

best_result = best_results[views_data.index(max(views_data))]

# print(f"# RETURN URL - {best_result[0]} - sorted, best results")
return best_result[0]

@staticmethod
def get_results(
Expand Down Expand Up @@ -107,62 +140,135 @@ def order_results(self, results: List[PyTube], song: Song) -> Dict[str, Any]:

# Slugify song title
slug_song_name = slugify(song.name)
slug_song_main_artist = slugify(song.artist)
slug_song_artists = slugify(", ".join(song.artists))
slug_song_title = slugify(
create_song_title(song.name, song.artists)
if not self.search_query
else create_search_query(song, self.search_query, False, None, True)
)

# DEBUG CODE
# print(f"#############################")
# print(f"slug_song_name: {slug_song_name}")
# print(f"slug_song_main_artist: {slug_song_main_artist}")
# print(f"slug_song_title: {slug_song_title}")
# print(f"slug_song_duration: {song.duration}")
# print(f"slug_song_artists: {slug_song_artists}")
# print(f"#############################")

for result in results:
# Skip results without id
if result.video_id is None:
continue

# Slugify some variables
# Slugify result title
slug_result_name = slugify(result.title)
sentence_words = slug_song_name.replace("-", " ").split(" ")
slug_result_channel = slugify(result.author)

# Check for common words in result name
# check for common words in result name
sentence_words = slug_song_name.split("-")
common_word = any(
word != "" and word in slug_result_name for word in sentence_words
)

# print("-----------------------------")
# print(f"sentence_words: {sentence_words}")
# print(f"common_word: {common_word}")
# print(f"result link: {result.watch_url}")
# print(f"result duration: {result.length}")
# print(f"slug_result_name: {slug_result_name}")
# print(f"slug_result_channel: {slug_result_channel}")
# print("-----------------------------")

# skip results that have no common words in their name
if not common_word:
continue

# Find artist match
artist_match_number = 0.0
artist_match = fuzz.ratio(
f"{slug_song_artists}-{slug_song_name}", slug_result_name
)

# Calculate artist match for each artist
# in the song's artist list
for artist in song.artists:
artist_match_number += fuzz.partial_token_sort_ratio(
slugify(artist), slug_result_name
# print(f"first artist match: {artist_match}")

if artist_match < 70:
# Try to use channel name instead
# with the main artist name
main_artist_match = fuzz.ratio(
slug_song_main_artist, slug_result_channel
)

slug_main_artist = slug_song_main_artist.replace("-", "")

main_artist_match = slug_main_artist in [
slug_result_name.replace("-", ""),
slug_result_channel.replace("-", ""),
]

# print(f"main_artist_match: {main_artist_match}")

# If the main artist name is in the channel name
# we add 30% to the artist match
if main_artist_match:
artist_match += 30
# print(f"new artist_match: {artist_match}")

# skip results with artist match lower than 70%
artist_match = artist_match_number / len(song.artists)
if artist_match < 70:
# print(f"! artist match lower than 70% {artist_match}, skipping")
continue

# print(f"final artist_match: {artist_match}")

# Calculate name match
name_match = fuzz.partial_token_sort_ratio(
slug_result_name, slug_song_title
)
test_str1 = slug_result_name
test_str2 = slug_song_title

# check if the artist is in the song name
# but not in the result name
# if it is, we add the artist to the result name
for artist in song.artists:
slug_song_artist = slugify(artist)
if slug_song_artist in test_str2 and not slug_song_artist in test_str1:
test_str1 += f"-{slug_song_artist}"

# same thing for for song name
for artist in song.artists:
slug_result_artist = slugify(artist)
if (
slug_result_artist in test_str1
and not slug_result_artist in test_str2
):
test_str2 += f"-{slug_result_artist}"

# calculate the name match
name_match = fuzz.ratio(test_str1, test_str2)

# Drop results with name match lower than 50%
if name_match < 50:
# print("! name_match < 50, skipping")
continue

# print(f"name_match: {name_match}")

# Calculate time match
time_match = (
100 - (result.length - song.duration**2) / song.duration * 100
time_match = 100 - (
((result.length - song.duration) ** 2) / song.duration * 100
)

# print(f"time_match: {time_match}")

# Drop results with time match lower than 50%
if time_match < 50:
# print("! time_match < 50, skipping")
continue

average_match = (artist_match + name_match + time_match) / 3

# print(f"average_match: {average_match}")

# the results along with the avg Match
links_with_match_value[result.watch_url] = average_match
links_with_match_value[result.watch_url] = (average_match, result.views)

return links_with_match_value
Loading

0 comments on commit a034397

Please sign in to comment.