remove functions from reference_playlists_to_library.py move to serverlibary
This commit is contained in:
1 parent
0a6457abe0
commit
f5d7962aba
3 files changed
+57
-45
No files matched your search
@@ -5,7 +5,8 @@ import re
|
|||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from difflib import SequenceMatcher
|
|
||||||
|
from serverlibrary import words, word_similarity, score_match_jaccard, score_match_fuzzy, combined_score
|
||||||
|
|
||||||
WORKDIR = Path("/data/navidrome/music/")
|
WORKDIR = Path("/data/navidrome/music/")
|
||||||
PLAYLIST_DIR = WORKDIR / "playlists"
|
PLAYLIST_DIR = WORKDIR / "playlists"
|
||||||
@@ -28,44 +29,6 @@ class CustomLogger(logging.Logger):
|
|||||||
logging.setLoggerClass(CustomLogger)
|
logging.setLoggerClass(CustomLogger)
|
||||||
logger: CustomLogger = logging.getLogger(__name__) # type: ignore[assignment]
|
logger: CustomLogger = logging.getLogger(__name__) # type: ignore[assignment]
|
||||||
|
|
||||||
|
|
||||||
def words(text):
|
|
||||||
return set(filter(None, re.split(r"[^a-z0-9]+", text.lower())))
|
|
||||||
|
|
||||||
|
|
||||||
def word_similarity(a: str, b: str) -> float:
|
|
||||||
return SequenceMatcher(None, a, b).ratio()
|
|
||||||
|
|
||||||
|
|
||||||
def score_match_jaccard(search_words, candidate_words):
|
|
||||||
intersection = search_words & candidate_words
|
|
||||||
union = search_words | candidate_words
|
|
||||||
|
|
||||||
if not union:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
return len(intersection) / len(union)
|
|
||||||
|
|
||||||
|
|
||||||
def score_match_fuzzy(search_words, candidate_words):
|
|
||||||
total = 0.0
|
|
||||||
|
|
||||||
for sw in search_words:
|
|
||||||
best = 0.0
|
|
||||||
|
|
||||||
for cw in candidate_words:
|
|
||||||
best = max(best, word_similarity(sw, cw))
|
|
||||||
|
|
||||||
total += best
|
|
||||||
|
|
||||||
# logger.trace(f"Total: {total}/{len(search_words)}")
|
|
||||||
return total / len(search_words)
|
|
||||||
|
|
||||||
|
|
||||||
def combined_score(score_fuzzy: float, score_jaccard: float) -> float:
|
|
||||||
return score_fuzzy * weight_fuzzy + score_jaccard * weight_jaccard
|
|
||||||
|
|
||||||
|
|
||||||
def find_folder(directory: Path, folder: str) -> list[Path]:
|
def find_folder(directory: Path, folder: str) -> list[Path]:
|
||||||
matches: list[tuple[Path, float]] = []
|
matches: list[tuple[Path, float]] = []
|
||||||
search_words = words(folder)
|
search_words = words(folder)
|
||||||
@@ -78,7 +41,7 @@ def find_folder(directory: Path, folder: str) -> list[Path]:
|
|||||||
|
|
||||||
score_fuzzy = score_match_fuzzy(search_words, candidate_words)
|
score_fuzzy = score_match_fuzzy(search_words, candidate_words)
|
||||||
score_jaccard = score_match_jaccard(search_words, candidate_words)
|
score_jaccard = score_match_jaccard(search_words, candidate_words)
|
||||||
score = combined_score(score_fuzzy, score_jaccard)
|
score = combined_score(score_fuzzy, score_jaccard, weight_fuzzy, weight_jaccard)
|
||||||
logger.trace(f"Candidate: {score:.2f}:{candidate_words}")
|
logger.trace(f"Candidate: {score:.2f}:{candidate_words}")
|
||||||
if score > folder_score_threshold:
|
if score > folder_score_threshold:
|
||||||
matches.append((candidate, score))
|
matches.append((candidate, score))
|
||||||
@@ -216,7 +179,7 @@ def find_replacement(old_path: Path):
|
|||||||
|
|
||||||
score_fuzzy = score_match_fuzzy(search_words, candidate_words)
|
score_fuzzy = score_match_fuzzy(search_words, candidate_words)
|
||||||
score_jaccard = score_match_jaccard(search_words, candidate_words)
|
score_jaccard = score_match_jaccard(search_words, candidate_words)
|
||||||
score = combined_score(score_fuzzy, score_jaccard)
|
score = combined_score(score_fuzzy, score_jaccard, weight_fuzzy, weight_jaccard)
|
||||||
logger.trace(
|
logger.trace(
|
||||||
f"{filename_structure} Words: {candidate_words} {score:.2f} / {best_score:.2f}, "
|
f"{filename_structure} Words: {candidate_words} {score:.2f} / {best_score:.2f}, "
|
||||||
f"{weight_fuzzy:.2} * {score_fuzzy:.2f} + {weight_jaccard:.2} * {score_jaccard:.2f} "
|
f"{weight_fuzzy:.2} * {score_fuzzy:.2f} + {weight_jaccard:.2} * {score_jaccard:.2f} "
|
||||||
|
|||||||
@@ -2,3 +2,8 @@ from .core import safe_rename
|
|||||||
from .core import safe_delete
|
from .core import safe_delete
|
||||||
from .core import get_audio_tracks
|
from .core import get_audio_tracks
|
||||||
from .core import get_subtitle_tracks
|
from .core import get_subtitle_tracks
|
||||||
|
from .core import words
|
||||||
|
from .core import word_similarity
|
||||||
|
from .core import score_match_jaccard
|
||||||
|
from .core import score_match_fuzzy
|
||||||
|
from .core import combined_score
|
||||||
+48
-4
@@ -5,6 +5,7 @@ import subprocess
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from difflib import SequenceMatcher
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
@@ -16,7 +17,8 @@ def safe_delete(path: Path):
|
|||||||
else:
|
else:
|
||||||
path.unlink()
|
path.unlink()
|
||||||
|
|
||||||
def safe_rename(src: Path, dst: Path, dry: bool=False, confirm: bool=False, silent=False):
|
def safe_rename(src: Path, dst: Path, dry: bool=False, confirm: bool=False,
|
||||||
|
silent=False,overwrite=False) -> (str, bool):
|
||||||
"""
|
"""
|
||||||
Safely rename a file or directory without overwriting existing files.
|
Safely rename a file or directory without overwriting existing files.
|
||||||
Automatically appends _1, _2, ... if needed.
|
Automatically appends _1, _2, ... if needed.
|
||||||
@@ -45,14 +47,18 @@ def safe_rename(src: Path, dst: Path, dry: bool=False, confirm: bool=False, sile
|
|||||||
print(ret)
|
print(ret)
|
||||||
|
|
||||||
if dry:
|
if dry:
|
||||||
return
|
return ret, True
|
||||||
|
|
||||||
if confirm:
|
if confirm:
|
||||||
input("Press Enter to confirm...")
|
input("Press Enter to confirm...")
|
||||||
|
|
||||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if dst.exists() and not overwrite:
|
||||||
|
print("File already exists...")
|
||||||
|
return "",False
|
||||||
src.rename(dst)
|
src.rename(dst)
|
||||||
return ret
|
return ret, True
|
||||||
|
|
||||||
def get_audio_tracks( source: str, title: int | None = None, handbrake: bool = False) -> list[dict]:
|
def get_audio_tracks( source: str, title: int | None = None, handbrake: bool = False) -> list[dict]:
|
||||||
"""
|
"""
|
||||||
@@ -197,4 +203,42 @@ def get_subtitle_tracks(source):
|
|||||||
"primary": False,
|
"primary": False,
|
||||||
})
|
})
|
||||||
|
|
||||||
return tracks
|
return tracks
|
||||||
|
|
||||||
|
|
||||||
|
def words(text):
|
||||||
|
return set(filter(None, re.split(r"[^a-z0-9]+", text.lower())))
|
||||||
|
|
||||||
|
|
||||||
|
def word_similarity(a: str, b: str) -> float:
|
||||||
|
return SequenceMatcher(None, a, b).ratio()
|
||||||
|
|
||||||
|
|
||||||
|
def score_match_jaccard(search_words, candidate_words):
|
||||||
|
intersection = search_words & candidate_words
|
||||||
|
union = search_words | candidate_words
|
||||||
|
|
||||||
|
if not union:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
return len(intersection) / len(union)
|
||||||
|
|
||||||
|
|
||||||
|
def score_match_fuzzy(search_words, candidate_words):
|
||||||
|
total = 0.0
|
||||||
|
|
||||||
|
for sw in search_words:
|
||||||
|
best = 0.0
|
||||||
|
|
||||||
|
for cw in candidate_words:
|
||||||
|
best = max(best, word_similarity(sw, cw))
|
||||||
|
|
||||||
|
total += best
|
||||||
|
|
||||||
|
# logger.trace(f"Total: {total}/{len(search_words)}")
|
||||||
|
return total / len(search_words)
|
||||||
|
|
||||||
|
|
||||||
|
def combined_score(score_fuzzy: float, score_jaccard: float,
|
||||||
|
weight_fuzzy: float, weight_jaccard: float) -> float:
|
||||||
|
return score_fuzzy * weight_fuzzy + score_jaccard * weight_jaccard
|
||||||
Reference in new issue
Block a user