Compare commits
7 Commits
a0c074adad
...
f3ccab513b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3ccab513b | ||
|
|
7819e863eb | ||
|
|
9f6eb2554a | ||
|
|
b5c792b8d8 | ||
|
|
2b48e889a5 | ||
|
|
688267834d | ||
|
|
c9a411d15d |
@ -10,6 +10,7 @@ from PyQt5.QtCore import (
|
||||
pyqtSignal,
|
||||
QEvent,
|
||||
QModelIndex,
|
||||
QObject,
|
||||
QSize,
|
||||
Qt,
|
||||
)
|
||||
@ -55,6 +56,7 @@ from models import (
|
||||
)
|
||||
|
||||
start_time_re = re.compile(r"@\d\d:\d\d:\d\d")
|
||||
HEADER_NOTES_COLUMN = 2
|
||||
|
||||
|
||||
class RowMeta:
|
||||
@ -96,6 +98,16 @@ class NoSelectDelegate(QStyledItemDelegate):
|
||||
return QPlainTextEdit(parent)
|
||||
return super().createEditor(parent, option, index)
|
||||
|
||||
def eventFilter(self, editor: QObject, event: QEvent):
|
||||
"""By default, QPlainTextEdit doesn't handle enter or return"""
|
||||
|
||||
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Return:
|
||||
if (Qt.ShiftModifier & event.modifiers()) != Qt.ShiftModifier:
|
||||
self.commitData.emit(editor)
|
||||
self.closeEditor.emit(editor)
|
||||
|
||||
return super().eventFilter(editor, event)
|
||||
|
||||
|
||||
class PlaylistTab(QTableWidget):
|
||||
# Qt.UserRoles
|
||||
@ -213,7 +225,7 @@ class PlaylistTab(QTableWidget):
|
||||
# rows. Check and fix:
|
||||
for row in range(drop_row, drop_row + len(rows_to_move)):
|
||||
if not self._get_row_track_id(row):
|
||||
self.setSpan(row, 1, 1, len(columns))
|
||||
self.setSpan(row, HEADER_NOTES_COLUMN, 1, len(columns))
|
||||
|
||||
# Scroll to drop zone
|
||||
self.scrollToItem(self.item(row, 1))
|
||||
@ -358,7 +370,7 @@ class PlaylistTab(QTableWidget):
|
||||
#
|
||||
# Call sequences:
|
||||
# Start editing:
|
||||
# edit() (called twice; not sure why)
|
||||
# edit()
|
||||
# _cell_edit_started()
|
||||
# End editing:
|
||||
# _cell_changed() (only if changes made)
|
||||
@ -408,8 +420,8 @@ class PlaylistTab(QTableWidget):
|
||||
editor: QWidget,
|
||||
hint: QAbstractItemDelegate.EndEditHint) -> None:
|
||||
"""
|
||||
Override QAbstractItemView.closeEditor to enable play controls
|
||||
and update display.
|
||||
Override PySide2.QAbstractItemView.closeEditor to enable
|
||||
play controls and update display.
|
||||
"""
|
||||
|
||||
# update_display to update start times, such as when a note has
|
||||
@ -426,7 +438,7 @@ class PlaylistTab(QTableWidget):
|
||||
trigger: QAbstractItemView.EditTrigger,
|
||||
event: QEvent) -> bool:
|
||||
"""
|
||||
Override QAbstractItemView.edit to catch when editing starts
|
||||
Override PySide2.QAbstractItemView.edit to catch when editing starts
|
||||
"""
|
||||
|
||||
result = super(PlaylistTab, self).edit(index, trigger, event)
|
||||
@ -450,16 +462,16 @@ class PlaylistTab(QTableWidget):
|
||||
self.edit_cell_type = "row_notes"
|
||||
else:
|
||||
# Can't edit other columns
|
||||
return
|
||||
return False
|
||||
|
||||
# Check whether we're editing a notes row for later
|
||||
if self.edit_cell_type == "row_notes":
|
||||
note_column = columns['row_notes'].idx
|
||||
else:
|
||||
# This is a section header. Text is always in row 1.
|
||||
if column != 1:
|
||||
return
|
||||
note_column = 1
|
||||
# This is a section header.
|
||||
if column != HEADER_NOTES_COLUMN:
|
||||
return False
|
||||
note_column = HEADER_NOTES_COLUMN
|
||||
self.edit_cell_type = "row_notes"
|
||||
|
||||
# Disable play controls so that keyboard input doesn't
|
||||
@ -561,7 +573,10 @@ class PlaylistTab(QTableWidget):
|
||||
|
||||
if row_data.track_id:
|
||||
# Add track details to items
|
||||
start_gap = row_data.track.start_gap
|
||||
try:
|
||||
start_gap = row_data.track.start_gap
|
||||
except:
|
||||
return
|
||||
start_gap_item = QTableWidgetItem(str(start_gap))
|
||||
if start_gap and start_gap >= 500:
|
||||
start_gap_item.setBackground(QColor(Config.COLOUR_LONG_START))
|
||||
@ -615,12 +630,16 @@ class PlaylistTab(QTableWidget):
|
||||
|
||||
# Make empty items (row background won't be coloured without
|
||||
# items present). Any notes should displayed starting in
|
||||
# column 0
|
||||
for i in range(2, len(columns)):
|
||||
# column 2 for now - bug in Qt means that when row size is
|
||||
# set, spanned columns are ignored, so put notes in col2
|
||||
# (typically title).
|
||||
for i in range(1, len(columns)):
|
||||
if i == 2:
|
||||
continue
|
||||
self.setItem(row, i, QTableWidgetItem())
|
||||
self.setSpan(row, 1, 1, len(columns))
|
||||
self.setSpan(row, HEADER_NOTES_COLUMN, 1, len(columns) - 1)
|
||||
notes_item = QTableWidgetItem(row_data.note)
|
||||
self.setItem(row, 1, notes_item)
|
||||
self.setItem(row, HEADER_NOTES_COLUMN, notes_item)
|
||||
|
||||
# Save (no) track_id
|
||||
userdata_item.setData(self.ROW_TRACK_ID, 0)
|
||||
@ -1001,8 +1020,8 @@ class PlaylistTab(QTableWidget):
|
||||
(self.item(row, columns['row_notes'].idx)
|
||||
.setBackground(QColor(note_colour)))
|
||||
|
||||
# Ensure content is visible by wrapping cells
|
||||
self.resizeRowToContents(row)
|
||||
# Ensure content is visible by wrapping cells
|
||||
self.resizeRowToContents(row)
|
||||
|
||||
# Highlight low bitrates
|
||||
if track.bitrate:
|
||||
@ -1102,11 +1121,11 @@ class PlaylistTab(QTableWidget):
|
||||
elif note_text.endswith("+"):
|
||||
section_start_plr = playlist_row
|
||||
section_time = 0
|
||||
self._set_row_colour(
|
||||
row, QColor(note_colour)
|
||||
)
|
||||
self._set_row_colour(row, QColor(note_colour))
|
||||
# Section headers are always bold
|
||||
self._set_row_bold(row)
|
||||
# Ensure content is visible by wrapping cells
|
||||
self.resizeRowToContents(row)
|
||||
continue
|
||||
|
||||
# Have we had a section start but not end?
|
||||
@ -1369,7 +1388,7 @@ class PlaylistTab(QTableWidget):
|
||||
if track_id:
|
||||
item_note = self.item(row, columns['row_notes'].idx)
|
||||
else:
|
||||
item_note = self.item(row, 1)
|
||||
item_note = self.item(row, HEADER_NOTES_COLUMN)
|
||||
return item_note.text()
|
||||
|
||||
def _get_row_start_time(self, row: int) -> Optional[datetime]:
|
||||
@ -1593,7 +1612,7 @@ class PlaylistTab(QTableWidget):
|
||||
for i in range(2, len(columns)):
|
||||
self.item(row, i).setText("")
|
||||
# Set note text in correct column for section head
|
||||
self.item(row, 1).setText(plr.note)
|
||||
self.item(row, HEADER_NOTES_COLUMN).setText(plr.note)
|
||||
# Remove row duration
|
||||
self._set_row_duration(row, 0)
|
||||
# Remote track_id from row
|
||||
@ -1741,16 +1760,19 @@ class PlaylistTab(QTableWidget):
|
||||
Set or reset row background colour
|
||||
"""
|
||||
|
||||
j: int
|
||||
column: int
|
||||
|
||||
if colour:
|
||||
brush = QBrush(colour)
|
||||
else:
|
||||
brush = QBrush()
|
||||
|
||||
for j in range(1, self.columnCount()):
|
||||
if self.item(row, j):
|
||||
self.item(row, j).setBackground(brush)
|
||||
for column in range(1, self.columnCount()):
|
||||
# Don't clear colour on start gap row
|
||||
if not colour and column == columns['start_gap'].idx:
|
||||
continue
|
||||
if self.item(row, column):
|
||||
self.item(row, column).setBackground(brush)
|
||||
|
||||
def _set_row_duration(self, row: int, ms: int) -> None:
|
||||
"""Set duration of this row in row metadata"""
|
||||
@ -1808,12 +1830,12 @@ class PlaylistTab(QTableWidget):
|
||||
additional_text: str) -> None:
|
||||
"""Append additional_text to row display"""
|
||||
|
||||
# Column to update is either 1 for a section header or the
|
||||
# appropriate row_notes column for a track row
|
||||
# Column to update is either HEADER_NOTES_COLUMN for a section
|
||||
# header or the appropriate row_notes column for a track row
|
||||
if playlist_row.track_id:
|
||||
column = columns['row_notes'].idx
|
||||
else:
|
||||
column = 1
|
||||
column = HEADER_NOTES_COLUMN
|
||||
|
||||
# Update text
|
||||
new_text = playlist_row.note + additional_text
|
||||
|
||||
@ -3,18 +3,10 @@
|
||||
# Script to replace existing files in parent directory. Typical usage:
|
||||
# the current directory contains a "better" version of the file than the
|
||||
# parent (eg, bettet bitrate).
|
||||
#
|
||||
# Actions:
|
||||
#
|
||||
# - check that the same filename is present in the parent directory
|
||||
# - check that the artist and title tags are the same
|
||||
# - append ".bak" to the version in the parent directory
|
||||
# - move file to parent directory
|
||||
# - normalise file
|
||||
# - update duration, start_gap, fade_at, silence_at, mtime in database
|
||||
|
||||
import glob
|
||||
import os
|
||||
import pydymenu # type: ignore
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
@ -33,182 +25,230 @@ from sqlalchemy.exc import IntegrityError
|
||||
from typing import List
|
||||
|
||||
# ###################### SETTINGS #########################
|
||||
process_multiple_matches = False
|
||||
do_processing = False
|
||||
process_no_matches = False
|
||||
process_name_and_tags_matches = True
|
||||
process_tag_matches = True
|
||||
do_processing = True
|
||||
process_no_matches = True
|
||||
|
||||
source_dir = '/home/kae/music/Singles/tmp'
|
||||
parent_dir = os.path.dirname(source_dir)
|
||||
# #########################################################
|
||||
|
||||
|
||||
def insensitive_glob(pattern):
|
||||
"""Helper for case insensitive glob.glob()"""
|
||||
|
||||
def either(c):
|
||||
return '[%s%s]' % (c.lower(), c.upper()) if c.isalpha() else c
|
||||
return glob.glob(''.join(map(either, pattern)))
|
||||
|
||||
|
||||
# Check file of same name exists in parent directory
|
||||
source_dir = '/home/kae/music/Singles/tmp' # os.getcwd()
|
||||
parent_dir = os.path.dirname(source_dir)
|
||||
assert source_dir != parent_dir
|
||||
|
||||
name_and_tags: List[str] = []
|
||||
name_not_tags: List[str] = []
|
||||
tags_not_name: List[str] = []
|
||||
multiple_similar: List[str] = []
|
||||
no_match: List[str] = []
|
||||
possibles: List[str] = []
|
||||
|
||||
print(f"{source_dir=}, {parent_dir=}")
|
||||
no_match: int = 0
|
||||
|
||||
|
||||
def main():
|
||||
global no_match
|
||||
|
||||
# We only want to run this against the production database because
|
||||
# we will affect files in the common pool of tracks used by all
|
||||
# databases
|
||||
if 'musicmuster_prod' not in os.environ.get('MM_DB'):
|
||||
response = input("Not on production database - c to continue: ")
|
||||
if response != "c":
|
||||
sys.exit(0)
|
||||
tracks = os.listdir(parent_dir)
|
||||
for fname in os.listdir(source_dir):
|
||||
parent_file = os.path.join(parent_dir, fname)
|
||||
new_file = os.path.join(source_dir, fname)
|
||||
us = get_tags(new_file)
|
||||
us_t = us['title']
|
||||
us_a = us['artist']
|
||||
|
||||
if os.path.exists(parent_file):
|
||||
# File exists, check tags
|
||||
p = get_tags(parent_file)
|
||||
p_t = p['title']
|
||||
p_a = p['artist']
|
||||
if (
|
||||
(str(p_t).lower() != str(us_t).lower()) or
|
||||
(str(p_a).lower() != str(us_a).lower())
|
||||
):
|
||||
name_not_tags.append(
|
||||
f" {fname=}, {p_t} → {us_t}, {p_a} → {us_a}")
|
||||
process_track(new_file, parent_file, us_t, us_a)
|
||||
# Sanity check
|
||||
assert source_dir != parent_dir
|
||||
|
||||
# Scan parent directory
|
||||
with Session() as session:
|
||||
all_tracks = Tracks.get_all(session)
|
||||
parent_tracks = [a for a in all_tracks if parent_dir in a.path]
|
||||
parent_fnames = [os.path.basename(a.path) for a in parent_tracks]
|
||||
# Create a dictionary of parent paths with their titles and
|
||||
# artists
|
||||
parents = {}
|
||||
for t in parent_tracks:
|
||||
parents[t.path] = {"title": t.title, "artist": t.artist}
|
||||
titles_to_path = {}
|
||||
artists_to_path = {}
|
||||
for k, v in parents.items():
|
||||
try:
|
||||
titles_to_path[v['title'].lower()] = k
|
||||
artists_to_path[v['artist'].lower()] = k
|
||||
except AttributeError:
|
||||
continue
|
||||
name_and_tags.append(new_file)
|
||||
process_track(new_file, parent_file, us_t, us_a)
|
||||
continue
|
||||
|
||||
for new_fname in os.listdir(source_dir):
|
||||
new_path = os.path.join(source_dir, new_fname)
|
||||
new_tags = get_tags(new_path)
|
||||
new_title = new_tags['title']
|
||||
new_artist = new_tags['artist']
|
||||
bitrate = new_tags['bitrate']
|
||||
|
||||
# If same filename exists in parent direcory, check tags
|
||||
parent_path = os.path.join(parent_dir, new_fname)
|
||||
if os.path.exists(parent_path):
|
||||
parent_tags = get_tags(parent_path)
|
||||
parent_title = parent_tags['title']
|
||||
parent_artist = parent_tags['artist']
|
||||
if (
|
||||
(str(parent_title).lower() == str(new_title).lower()) and
|
||||
(str(parent_artist).lower() == str(new_artist).lower())
|
||||
):
|
||||
name_and_tags.append(
|
||||
f" {new_fname=}, {parent_title} → {new_title}, "
|
||||
f" {parent_artist} → {new_artist}"
|
||||
)
|
||||
if process_name_and_tags_matches:
|
||||
process_track(new_path, parent_path, new_title,
|
||||
new_artist, bitrate)
|
||||
continue
|
||||
|
||||
# Check for matching tags although filename is different
|
||||
if new_title.lower() in titles_to_path:
|
||||
possible_path = titles_to_path[new_title.lower()]
|
||||
if parents[possible_path]['artist'].lower() == new_artist.lower():
|
||||
# print(
|
||||
# f"title={new_title}, artist={new_artist}:\n"
|
||||
# f" {new_path} → {parent_path}"
|
||||
# )
|
||||
tags_not_name.append(
|
||||
f"title={new_title}, artist={new_artist}:\n"
|
||||
f" {new_path} → {parent_path}"
|
||||
)
|
||||
if process_tag_matches:
|
||||
process_track(new_path, possible_path, new_title,
|
||||
new_artist, bitrate)
|
||||
continue
|
||||
else:
|
||||
no_match += 1
|
||||
|
||||
else:
|
||||
no_match += 1
|
||||
|
||||
# Try to find a near match
|
||||
stem = fname.split(".")[0]
|
||||
matches = insensitive_glob(os.path.join(parent_dir, stem) + '*')
|
||||
match_count = len(matches)
|
||||
if match_count == 0:
|
||||
if process_no_matches:
|
||||
print(f"\n file={fname}\n title={us_t}\n artist={us_a}\n")
|
||||
# Try fuzzy search
|
||||
d = {}
|
||||
while True:
|
||||
for i, match in enumerate(
|
||||
[a[0] for a in process.extract(fname, tracks, limit=5)]
|
||||
):
|
||||
d[i] = match
|
||||
for k, v in d.items():
|
||||
print(f"{k}: {v}")
|
||||
data = input("pick one, return to quit: ")
|
||||
if data == "":
|
||||
no_match.append(f"{fname}, {us_t=}, {us_a=}")
|
||||
break
|
||||
try:
|
||||
key = int(data)
|
||||
except ValueError:
|
||||
continue
|
||||
if key in d:
|
||||
old_file = os.path.join(parent_dir, d[key])
|
||||
oldtags = get_tags(old_file)
|
||||
old_title = oldtags['title']
|
||||
old_artist = oldtags['artist']
|
||||
print()
|
||||
print(f" Title tag will change {old_title} → {us_t}")
|
||||
print(f" Artist tag will change {old_artist} → {us_a}")
|
||||
print()
|
||||
data = input("Go ahead (y to accept)? ")
|
||||
if data == "y":
|
||||
process_track(new_file, old_file, us_t, us_a)
|
||||
break
|
||||
else:
|
||||
no_match.append(f"{fname}, {us_t=}, {us_a=}")
|
||||
continue
|
||||
no_match.append(f"{fname}, {us_t=}, {us_a=}")
|
||||
continue
|
||||
else:
|
||||
no_match.append(f"{fname}, {us_t=}, {us_a=}")
|
||||
continue
|
||||
|
||||
if match_count > 1:
|
||||
multiple_similar.append(fname + "\n " + "\n ".join(matches))
|
||||
if match_count <= 26 and process_multiple_matches:
|
||||
print(f"\n file={fname}\n title={us_t}\n artist={us_a}\n")
|
||||
d = {}
|
||||
while True:
|
||||
for i, match in enumerate(matches):
|
||||
d[i] = match
|
||||
for k, v in d.items():
|
||||
print(f"{k}: {v}")
|
||||
data = input("pick one, return to quit: ")
|
||||
if data == "":
|
||||
break
|
||||
try:
|
||||
key = int(data)
|
||||
except ValueError:
|
||||
continue
|
||||
if key in d:
|
||||
dst = d[key]
|
||||
process_track(new_file, dst, us_t, us_a)
|
||||
break
|
||||
else:
|
||||
continue
|
||||
continue # from break after testing for "" in data
|
||||
# One match, check tags
|
||||
sim_name = matches[0]
|
||||
p = get_tags(sim_name)
|
||||
p_t = p['title']
|
||||
p_a = p['artist']
|
||||
if (
|
||||
(str(p_t).lower() != str(us_t).lower()) or
|
||||
(str(p_a).lower() != str(us_a).lower())
|
||||
):
|
||||
possibles.append(
|
||||
f"File: {os.path.basename(sim_name)} → {fname}"
|
||||
f"\n {p_t} → {us_t}\n {p_a} → {us_a}"
|
||||
)
|
||||
process_track(new_file, sim_name, us_t, us_a)
|
||||
continue
|
||||
tags_not_name.append(f"Rename {os.path.basename(sim_name)} → {fname}")
|
||||
process_track(new_file, sim_name, us_t, us_a)
|
||||
# stem = new_fname.split(".")[0]
|
||||
# matches = insensitive_glob(os.path.join(parent_dir, stem) + '*')
|
||||
# match_count = len(matches)
|
||||
# if match_count == 0:
|
||||
if process_no_matches:
|
||||
prompt = f"\n file={new_fname}\n title={new_title}\n artist={new_artist}: "
|
||||
# Use fzf to search
|
||||
choice = pydymenu.fzf(parent_fnames, prompt)
|
||||
if choice:
|
||||
old_file = os.path.join(parent_dir, choice[0])
|
||||
oldtags = get_tags(old_file)
|
||||
old_title = oldtags['title']
|
||||
old_artist = oldtags['artist']
|
||||
print()
|
||||
print(f" File name will change {choice[0]}")
|
||||
print(f" → {new_fname}")
|
||||
print()
|
||||
print(f" Title tag will change {old_title}")
|
||||
print(f" → {new_title}")
|
||||
print()
|
||||
print(f" Artist tag will change {old_artist}")
|
||||
print(f" → {new_artist}")
|
||||
print()
|
||||
data = input("Go ahead (y to accept)? ")
|
||||
if data == "y":
|
||||
process_track(new_path, old_file, new_title, new_artist, bitrate)
|
||||
continue
|
||||
if data == "q":
|
||||
sys.exit(0)
|
||||
else:
|
||||
continue
|
||||
# else:
|
||||
# no_match.append(f"{new_fname}, {new_title=}, {new_artist=}")
|
||||
# continue
|
||||
|
||||
# if match_count > 1:
|
||||
# multiple_similar.append(new_fname + "\n " + "\n ".join(matches))
|
||||
# if match_count <= 26 and process_multiple_matches:
|
||||
# print(f"\n file={new_fname}\n title={new_title}\n artist={new_artist}\n")
|
||||
# d = {}
|
||||
# while True:
|
||||
# for i, match in enumerate(matches):
|
||||
# d[i] = match
|
||||
# for k, v in d.items():
|
||||
# print(f"{k}: {v}")
|
||||
# data = input("pick one, return to quit: ")
|
||||
# if data == "":
|
||||
# break
|
||||
# try:
|
||||
# key = int(data)
|
||||
# except ValueError:
|
||||
# continue
|
||||
# if key in d:
|
||||
# dst = d[key]
|
||||
# process_track(new_path, dst, new_title, new_artist, bitrate)
|
||||
# break
|
||||
# else:
|
||||
# continue
|
||||
# continue # from break after testing for "" in data
|
||||
# # One match, check tags
|
||||
# sim_name = matches[0]
|
||||
# p = get_tags(sim_name)
|
||||
# parent_title = p['title']
|
||||
# parent_artist = p['artist']
|
||||
# if (
|
||||
# (str(parent_title).lower() != str(new_title).lower()) or
|
||||
# (str(parent_artist).lower() != str(new_artist).lower())
|
||||
# ):
|
||||
# possibles.append(
|
||||
# f"File: {os.path.basename(sim_name)} → {new_fname}"
|
||||
# f"\n {parent_title} → {new_title}\n {parent_artist} → {new_artist}"
|
||||
# )
|
||||
# process_track(new_path, sim_name, new_title, new_artist, bitrate)
|
||||
# continue
|
||||
# tags_not_name.append(f"Rename {os.path.basename(sim_name)} → {new_fname}")
|
||||
# process_track(new_path, sim_name, new_title, new_artist, bitrate)
|
||||
|
||||
print(f"Name and tags match ({len(name_and_tags)}):")
|
||||
# print(" \n".join(name_and_tags))
|
||||
# print()
|
||||
|
||||
print(f"Name but not tags match ({len(name_not_tags)}):")
|
||||
print(" \n".join(name_not_tags))
|
||||
print()
|
||||
# print(f"Name but not tags match ({len(name_not_tags)}):")
|
||||
# print(" \n".join(name_not_tags))
|
||||
# print()
|
||||
|
||||
print(f"Tags but not name match ({len(tags_not_name)}):")
|
||||
# print(" \n".join(tags_not_name))
|
||||
# print()
|
||||
|
||||
print(f"Multiple similar names ({len(multiple_similar)}):")
|
||||
print(" \n".join(multiple_similar))
|
||||
print()
|
||||
# print(f"Multiple similar names ({len(multiple_similar)}):")
|
||||
# print(" \n".join(multiple_similar))
|
||||
# print()
|
||||
|
||||
print(f"Possibles: ({len(possibles)}):")
|
||||
print(" \n".join(possibles))
|
||||
print()
|
||||
# print(f"Possibles: ({len(possibles)}):")
|
||||
# print(" \n".join(possibles))
|
||||
# print()
|
||||
|
||||
print(f"No match ({len(no_match)}):")
|
||||
print(" \n".join(no_match))
|
||||
print()
|
||||
# print(f"No match ({len(no_match)}):")
|
||||
# print(" \n".join(no_match))
|
||||
# print()
|
||||
|
||||
# print(f"Name and tags match ({len(name_and_tags)}):")
|
||||
# print(f"Name but not tags match ({len(name_not_tags)}):")
|
||||
# print(f"Tags but not name match ({len(tags_not_name)}):")
|
||||
# print(f"Multiple similar names ({len(multiple_similar)}):")
|
||||
# print(f"Possibles: ({len(possibles)}):")
|
||||
# print(f"No match ({len(no_match)}):")
|
||||
print(f"No matches: {no_match}")
|
||||
|
||||
|
||||
def process_track(src, dst, title, artist):
|
||||
def process_track(src, dst, title, artist, bitrate):
|
||||
|
||||
new_path = os.path.join(os.path.dirname(dst), os.path.basename(src))
|
||||
print(
|
||||
f"process_track:\n {src=}\n {new_path=}\n "
|
||||
f"{dst=}\n {title=}, {artist=}\n"
|
||||
f"process_track:\n {src=}\n {dst=}\n {title=}, {artist=}\n"
|
||||
)
|
||||
|
||||
if not do_processing:
|
||||
@ -220,6 +260,7 @@ def process_track(src, dst, title, artist):
|
||||
track.title = title
|
||||
track.artist = artist
|
||||
track.path = new_path
|
||||
track.bitrate = bitrate
|
||||
try:
|
||||
session.commit()
|
||||
except IntegrityError:
|
||||
@ -228,6 +269,7 @@ def process_track(src, dst, title, artist):
|
||||
track.title = title
|
||||
track.artist = artist
|
||||
track.path = "DUMMY"
|
||||
track.bitrate = bitrate
|
||||
session.commit()
|
||||
track.path = new_path
|
||||
session.commit()
|
||||
|
||||
44
poetry.lock
generated
44
poetry.lock
generated
@ -73,6 +73,17 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "commonmark"
|
||||
version = "0.9.1"
|
||||
description = "Python parser for the CommonMark Markdown spec"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.1.1"
|
||||
@ -366,6 +377,17 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8,<4.0"
|
||||
|
||||
[[package]]
|
||||
name = "pydymenu"
|
||||
version = "0.5.2"
|
||||
description = "A pythonic wrapper interface for fzf, dmenu, and rofi."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
rich = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pyfzf"
|
||||
version = "0.3.1"
|
||||
@ -378,7 +400,7 @@ python-versions = "*"
|
||||
name = "pygments"
|
||||
version = "2.11.2"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
@ -519,6 +541,21 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "12.5.1"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.3,<4.0.0"
|
||||
|
||||
[package.dependencies]
|
||||
commonmark = ">=0.9.0,<0.10.0"
|
||||
pygments = ">=2.6.0,<3.0.0"
|
||||
|
||||
[package.extras]
|
||||
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
@ -671,7 +708,7 @@ python-versions = "*"
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "b181eb743e8b6c9cb7e03c4db0bcef425fe410d2ec3c4c801ce20e448a26f166"
|
||||
content-hash = "91e055875df86707e1ce1544b1d29126265011d750897912daa37af3fe005498"
|
||||
|
||||
[metadata.files]
|
||||
alembic = [
|
||||
@ -702,6 +739,7 @@ colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
commonmark = []
|
||||
decorator = [
|
||||
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
|
||||
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
||||
@ -944,6 +982,7 @@ pydub = [
|
||||
{file = "pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f"},
|
||||
]
|
||||
pydub-stubs = []
|
||||
pydymenu = []
|
||||
pyfzf = []
|
||||
pygments = [
|
||||
{file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"},
|
||||
@ -1020,6 +1059,7 @@ python-vlc = [
|
||||
{file = "python-vlc-3.0.16120.tar.gz", hash = "sha256:92f98fee088f72bd6d063b3b3312d0bd29b37e7ad65ddeb3a7303320300c2807"},
|
||||
{file = "python_vlc-3.0.16120-py3-none-any.whl", hash = "sha256:c409afb38fe9f788a663b4302ca583f31289ef0860ab2b1668da96bbe8f14bfc"},
|
||||
]
|
||||
rich = []
|
||||
six = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
|
||||
@ -22,6 +22,7 @@ python-slugify = "^6.1.2"
|
||||
thefuzz = "^0.19.0"
|
||||
python-Levenshtein = "^0.12.2"
|
||||
pyfzf = "^0.3.1"
|
||||
pydymenu = "^0.5.2"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
ipdb = "^0.13.9"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user