SQLA2: WIP, playlists load
This commit is contained in:
parent
caed7fd079
commit
4f03306aff
103
app/helpers.py
103
app/helpers.py
@ -1,8 +1,8 @@
|
|||||||
# import os
|
import os
|
||||||
# import psutil
|
# import psutil
|
||||||
#
|
#
|
||||||
# from config import Config
|
# from config import Config
|
||||||
# from datetime import datetime
|
from datetime import datetime
|
||||||
# from pydub import AudioSegment
|
# from pydub import AudioSegment
|
||||||
# from PyQt5.QtWidgets import QMessageBox
|
# from PyQt5.QtWidgets import QMessageBox
|
||||||
# from tinytag import TinyTag
|
# from tinytag import TinyTag
|
||||||
@ -44,7 +44,22 @@
|
|||||||
# # if there is no trailing silence, return lenght of track (it's less
|
# # if there is no trailing silence, return lenght of track (it's less
|
||||||
# # the chunk_size, but for chunk_size = 10ms, this may be ignored)
|
# # the chunk_size, but for chunk_size = 10ms, this may be ignored)
|
||||||
# return int(trim_ms)
|
# return int(trim_ms)
|
||||||
#
|
|
||||||
|
|
||||||
|
def file_is_readable(path: str, check_colon: bool = True) -> bool:
|
||||||
|
"""
|
||||||
|
Returns True if passed path is readable, else False
|
||||||
|
|
||||||
|
vlc cannot read files with a colon in the path
|
||||||
|
"""
|
||||||
|
|
||||||
|
if os.access(path, os.R_OK):
|
||||||
|
if check_colon:
|
||||||
|
return ':' not in path
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
#
|
#
|
||||||
# def get_audio_segment(path: str) -> Optional[AudioSegment]:
|
# def get_audio_segment(path: str) -> Optional[AudioSegment]:
|
||||||
# try:
|
# try:
|
||||||
@ -70,47 +85,47 @@
|
|||||||
# path=path
|
# path=path
|
||||||
# )
|
# )
|
||||||
# return d
|
# return d
|
||||||
#
|
|
||||||
#
|
|
||||||
# def get_relative_date(past_date: datetime, reference_date: datetime = None) \
|
def get_relative_date(past_date: datetime, reference_date: datetime = None) \
|
||||||
# -> str:
|
-> str:
|
||||||
# """
|
"""
|
||||||
# Return how long before reference_date past_date is as string.
|
Return how long before reference_date past_date is as string.
|
||||||
#
|
|
||||||
# Params:
|
Params:
|
||||||
# @past_date: datetime
|
@past_date: datetime
|
||||||
# @reference_date: datetime, defaults to current date and time
|
@reference_date: datetime, defaults to current date and time
|
||||||
#
|
|
||||||
# @return: string
|
@return: string
|
||||||
# """
|
"""
|
||||||
#
|
|
||||||
# if not past_date:
|
if not past_date:
|
||||||
# return "Never"
|
return "Never"
|
||||||
# if not reference_date:
|
if not reference_date:
|
||||||
# reference_date = datetime.now()
|
reference_date = datetime.now()
|
||||||
#
|
|
||||||
# # Check parameters
|
# Check parameters
|
||||||
# if past_date > reference_date:
|
if past_date > reference_date:
|
||||||
# return "get_relative_date() past_date is after relative_date"
|
return "get_relative_date() past_date is after relative_date"
|
||||||
#
|
|
||||||
# days: int
|
days: int
|
||||||
# days_str: str
|
days_str: str
|
||||||
# weeks: int
|
weeks: int
|
||||||
# weeks_str: str
|
weeks_str: str
|
||||||
#
|
|
||||||
# weeks, days = divmod((reference_date.date() - past_date.date()).days, 7)
|
weeks, days = divmod((reference_date.date() - past_date.date()).days, 7)
|
||||||
# if weeks == days == 0:
|
if weeks == days == 0:
|
||||||
# # Played today, so return time instead
|
# Same day so return time instead
|
||||||
# return past_date.strftime("%H:%M")
|
return past_date.strftime("%H:%M")
|
||||||
# if weeks == 1:
|
if weeks == 1:
|
||||||
# weeks_str = "week"
|
weeks_str = "week"
|
||||||
# else:
|
else:
|
||||||
# weeks_str = "weeks"
|
weeks_str = "weeks"
|
||||||
# if days == 1:
|
if days == 1:
|
||||||
# days_str = "day"
|
days_str = "day"
|
||||||
# else:
|
else:
|
||||||
# days_str = "days"
|
days_str = "days"
|
||||||
# return f"{weeks} {weeks_str}, {days} {days_str} ago"
|
return f"{weeks} {weeks_str}, {days} {days_str} ago"
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# def leading_silence(
|
# def leading_silence(
|
||||||
|
|||||||
@ -232,19 +232,22 @@ class Playdates(Base):
|
|||||||
# self.track_id = track_id
|
# self.track_id = track_id
|
||||||
# session.add(self)
|
# session.add(self)
|
||||||
# session.flush()
|
# session.flush()
|
||||||
#
|
|
||||||
# @staticmethod
|
@staticmethod
|
||||||
# def last_played(session: Session, track_id: int) -> Optional[datetime]:
|
def last_played(session: Session, track_id: int) -> Optional[datetime]:
|
||||||
# """Return datetime track last played or None"""
|
"""Return datetime track last played or None"""
|
||||||
#
|
|
||||||
# last_played: Optional[Playdates] = session.query(
|
last_played = session.execute(
|
||||||
# Playdates.lastplayed).filter(
|
select(Playdates.lastplayed)
|
||||||
# (Playdates.track_id == track_id)
|
.where(Playdates.track_id == track_id)
|
||||||
# ).order_by(Playdates.lastplayed.desc()).first()
|
.order_by(Playdates.lastplayed.desc())
|
||||||
# if last_played:
|
.limit(1)
|
||||||
# return last_played[0]
|
).first()
|
||||||
# else:
|
|
||||||
# return None
|
if last_played:
|
||||||
|
return last_played[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
#
|
#
|
||||||
# @staticmethod
|
# @staticmethod
|
||||||
# def played_after(session: Session, since: datetime) -> List["Playdates"]:
|
# def played_after(session: Session, since: datetime) -> List["Playdates"]:
|
||||||
|
|||||||
747
app/playlists.py
747
app/playlists.py
@ -1,15 +1,19 @@
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
# from enum import Enum, auto
|
# from enum import Enum, auto
|
||||||
|
from typing import List, Optional
|
||||||
# from typing import Dict, List, Optional, Set, Tuple, Union
|
# from typing import Dict, List, Optional, Set, Tuple, Union
|
||||||
#
|
#
|
||||||
# from PyQt5 import QtCore
|
# from PyQt5 import QtCore
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
# from PyQt5.Qt import QFont
|
from PyQt5.Qt import QFont
|
||||||
# from PyQt5.QtGui import QColor, QDropEvent
|
from PyQt5.QtGui import (
|
||||||
|
QColor,
|
||||||
|
# QDropEvent
|
||||||
|
)
|
||||||
# from PyQt5 import QtWidgets
|
# from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
# QAbstractItemView,
|
QAbstractItemView,
|
||||||
# QApplication,
|
# QApplication,
|
||||||
# QInputDialog,
|
# QInputDialog,
|
||||||
# QLineEdit,
|
# QLineEdit,
|
||||||
@ -28,29 +32,33 @@ import helpers
|
|||||||
# import threading
|
# import threading
|
||||||
#
|
#
|
||||||
from config import Config
|
from config import Config
|
||||||
# from datetime import datetime, timedelta
|
from datetime import datetime #, timedelta
|
||||||
# from helpers import get_relative_date, open_in_audacity
|
from helpers import (
|
||||||
|
get_relative_date,
|
||||||
|
# open_in_audacity
|
||||||
|
)
|
||||||
# from log import log.debug, log.error
|
# from log import log.debug, log.error
|
||||||
from models import (
|
from models import (
|
||||||
# Notes,
|
# Notes,
|
||||||
# Playdates,
|
Playdates,
|
||||||
Playlists,
|
Playlists,
|
||||||
PlaylistRows,
|
PlaylistRows,
|
||||||
Settings,
|
Settings,
|
||||||
# Tracks,
|
Tracks,
|
||||||
# NoteColours
|
# NoteColours
|
||||||
)
|
)
|
||||||
from dbconfig import Session
|
from dbconfig import Session
|
||||||
# start_time_re = re.compile(r"@\d\d:\d\d:\d\d")
|
# start_time_re = re.compile(r"@\d\d:\d\d:\d\d")
|
||||||
#
|
|
||||||
#
|
|
||||||
# class RowMeta:
|
class RowMeta:
|
||||||
# CLEAR = 0
|
CLEAR = 0
|
||||||
# NOTE = 1
|
NOTE = 1
|
||||||
# UNREADABLE = 2
|
UNREADABLE = 2
|
||||||
# NEXT = 3
|
NEXT = 3
|
||||||
# CURRENT = 4
|
CURRENT = 4
|
||||||
# PLAYED = 5
|
PLAYED = 5
|
||||||
|
|
||||||
|
|
||||||
# Columns
|
# Columns
|
||||||
Column = namedtuple("Column", ['idx', 'heading'])
|
Column = namedtuple("Column", ['idx', 'heading'])
|
||||||
@ -89,8 +97,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
# Qt.UserRoles
|
# Qt.UserRoles
|
||||||
ROW_METADATA = Qt.UserRole
|
ROW_METADATA = Qt.UserRole
|
||||||
# CONTENT_OBJECT = Qt.UserRole + 1
|
ROW_TRACK_ID = Qt.UserRole + 1
|
||||||
ROW_DURATION = Qt.UserRole + 2
|
PLAYLISTROW_ID = Qt.UserRole + 2
|
||||||
|
|
||||||
def __init__(self, musicmuster: QMainWindow, session: Session,
|
def __init__(self, musicmuster: QMainWindow, session: Session,
|
||||||
playlist_id: int, *args, **kwargs):
|
playlist_id: int, *args, **kwargs):
|
||||||
@ -144,7 +152,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
#
|
#
|
||||||
# self.itemSelectionChanged.connect(self._select_event)
|
# self.itemSelectionChanged.connect(self._select_event)
|
||||||
#
|
#
|
||||||
# self.row_filter: Optional[str] = None
|
self.row_filter: Optional[str] = None
|
||||||
# self.editing_cell: bool = False
|
# self.editing_cell: bool = False
|
||||||
# self.selecting_in_progress = False
|
# self.selecting_in_progress = False
|
||||||
# Connect signals
|
# Connect signals
|
||||||
@ -394,15 +402,14 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
duration_item = QTableWidgetItem(
|
duration_item = QTableWidgetItem(
|
||||||
helpers.ms_to_mmss(row_data.track.duration))
|
helpers.ms_to_mmss(row_data.track.duration))
|
||||||
self._set_row_duration(row, row_data.track.duration)
|
|
||||||
|
|
||||||
last_playtime = Playdates.last_played(session, row_data.track.id)
|
last_playtime = Playdates.last_played(session, row_data.track.id)
|
||||||
last_played_str = get_relative_date(last_playtime)
|
last_played_str = get_relative_date(last_playtime)
|
||||||
last_played_item = QTableWidgetItem(last_played_str)
|
last_played_item = QTableWidgetItem(last_played_str)
|
||||||
self.setItem(row, columns['lastplayed'], last_played_item)
|
self.setItem(row, columns['lastplayed'].idx, last_played_item)
|
||||||
|
|
||||||
# Mark track if file is unreadable
|
# Mark track if file is unreadable
|
||||||
if not self._file_is_readable(row_data.track.path):
|
if not helpers.file_is_readable(row_data.track.path):
|
||||||
self._set_unreadable_row(row)
|
self._set_unreadable_row(row)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -423,9 +430,18 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.setItem(row, columns['end_time'].idx, end_item)
|
self.setItem(row, columns['end_time'].idx, end_item)
|
||||||
self.setItem(row, columns['row_notes'].idx, notes_item)
|
self.setItem(row, columns['row_notes'].idx, notes_item)
|
||||||
|
|
||||||
|
# Save track_id and playlistrow_id
|
||||||
|
userdata_item = QTableWidgetItem()
|
||||||
|
if row_data.track_id:
|
||||||
|
userdata_item.setData(self.ROW_TRACK_ID, row_data.track_id)
|
||||||
|
else:
|
||||||
|
userdata_item.setData(self.ROW_TRACK_ID, 0)
|
||||||
|
userdata_item.setData(self.PLAYLISTROW_ID, row_data.id)
|
||||||
|
self.setItem(row, columns['userdata'].idx, userdata_item)
|
||||||
|
|
||||||
|
# Span note across table
|
||||||
if not row_data.track_id:
|
if not row_data.track_id:
|
||||||
# Span note across table
|
self.setSpan(row, columns['row_notes'].idx, len(columns), 1)
|
||||||
self.setSpan(row, 0, len(columns), 1)
|
|
||||||
|
|
||||||
# Scroll to new row
|
# Scroll to new row
|
||||||
self.scrollToItem(title_item, QAbstractItemView.PositionAtCenter)
|
self.scrollToItem(title_item, QAbstractItemView.PositionAtCenter)
|
||||||
@ -815,211 +831,200 @@ class PlaylistTab(QTableWidget):
|
|||||||
#
|
#
|
||||||
# with Session() as session:
|
# with Session() as session:
|
||||||
# self._set_next(row, session)
|
# self._set_next(row, session)
|
||||||
#
|
|
||||||
# def update_display(self, session, clear_selection: bool = True) -> None:
|
def update_display(self, session, clear_selection: bool = True) -> None:
|
||||||
# """
|
"""
|
||||||
# Set row colours, fonts, etc
|
Set row colours, fonts, etc
|
||||||
#
|
|
||||||
# Actions required:
|
Actions required:
|
||||||
# - Clear selection if required
|
- Clear selection if required
|
||||||
# - Render notes in correct colour
|
- Render notes in correct colour
|
||||||
# - Render current, next and unplayable tracks in correct colour
|
- Render current, next and unplayable tracks in correct colour
|
||||||
# - Set start and end times
|
- Set start and end times
|
||||||
# - Show unplayed tracks in bold
|
- Show unplayed tracks in bold
|
||||||
# """
|
"""
|
||||||
#
|
|
||||||
# # Clear selection if required
|
# Clear selection if required
|
||||||
# if clear_selection:
|
if clear_selection:
|
||||||
# self.clearSelection()
|
self.clearSelection()
|
||||||
#
|
|
||||||
# current_row: Optional[int] = self._get_current_track_row()
|
current_row: Optional[int] = self._get_current_track_row()
|
||||||
# next_row: Optional[int] = self._get_next_track_row()
|
next_row: Optional[int] = self._get_next_track_row()
|
||||||
# notes: List[int] = self._get_notes_rows()
|
played: Optional[List[int]] = self._get_played_track_rows()
|
||||||
# played: Optional[List[int]] = self._get_played_track_rows()
|
unreadable: List[int] = self._get_unreadable_track_rows()
|
||||||
# unreadable: List[int] = self._get_unreadable_track_rows()
|
|
||||||
#
|
if self.row_filter:
|
||||||
# if self.row_filter:
|
filter_text = self.row_filter.lower()
|
||||||
# filter_text = self.row_filter.lower()
|
else:
|
||||||
# else:
|
filter_text = None
|
||||||
# filter_text = None
|
next_start_time = None
|
||||||
# hide_played = self.musicmuster.hide_played_tracks
|
section_start_row = None
|
||||||
# last_played_str: str
|
section_time = 0
|
||||||
# last_playedtime: Optional[datetime]
|
|
||||||
# next_start_time: Optional[datetime] = None
|
# Start time calculations
|
||||||
# note_colour: str
|
# Don't change start times for tracks that have been played.
|
||||||
# note_start_time: Optional[str]
|
# For unplayed tracks, if there's a 'current' or 'next'
|
||||||
# note_text: str
|
# track marked, populate start times from then onwards. A note
|
||||||
# row: int
|
# with a start time will reset the next track start time.
|
||||||
# row_time: Optional[datetime]
|
|
||||||
# section_start_row: Optional[int] = None
|
# Cycle through all rows
|
||||||
# section_time: int = 0
|
for row in range(self.rowCount()):
|
||||||
# start_time: Optional[datetime]
|
|
||||||
# start_times_row: Optional[int]
|
# Get track if there is one
|
||||||
# track: Optional[Tracks]
|
track_id = self._get_row_track_id(row)
|
||||||
#
|
track = None
|
||||||
# # Start time calculations
|
if track_id:
|
||||||
# # Don't change start times for tracks that have been played.
|
track = session.get(Tracks, track_id)
|
||||||
# # For unplayed tracks, if there's a 'current' or 'next'
|
|
||||||
# # track marked, populate start times from then onwards. A note
|
if track:
|
||||||
# # with a start time will reset the next track start time.
|
# Render unplayable tracks in correct colour
|
||||||
#
|
if not helpers.file_is_readable(track.path):
|
||||||
# # Cycle through all rows
|
self._set_row_colour(row, QColor(Config.COLOUR_UNREADABLE))
|
||||||
# for row in range(self.rowCount()):
|
self._set_row_bold(row)
|
||||||
#
|
continue
|
||||||
# # Render notes in correct colour
|
|
||||||
# if row in notes:
|
# Add track time to section time if in timed section
|
||||||
# # Extract note text from database to ignore section timings
|
if section_start_row is not None:
|
||||||
# note_text = self._get_row_notes_object(row, session).note
|
section_time += track.duration
|
||||||
# if filter_text:
|
|
||||||
# if filter_text not in note_text.lower():
|
# If filtering, only show matching tracks
|
||||||
# self.hideRow(row)
|
if filter_text:
|
||||||
# continue
|
try:
|
||||||
# else:
|
if (track.title
|
||||||
# self.showRow(row)
|
and filter_text not in track.title.lower()
|
||||||
# else:
|
and track.artist
|
||||||
# self.showRow(row)
|
and filter_text not in track.artist.lower()):
|
||||||
# # Does the note have a start time?
|
self.hideRow(row)
|
||||||
# row_time = self._get_note_text_time(note_text)
|
continue
|
||||||
# if row_time:
|
else:
|
||||||
# next_start_time = row_time
|
self.showRow(row)
|
||||||
# # Does it delimit a section?
|
except TypeError:
|
||||||
# if section_start_row is not None:
|
print(f"TypeError: {track=}")
|
||||||
# if note_text.endswith("-"):
|
else:
|
||||||
# self._set_timed_section(session, section_start_row,
|
self.showRow(row)
|
||||||
# section_time)
|
|
||||||
# section_start_row = None
|
# Render playing track
|
||||||
# section_time = 0
|
if row == current_row:
|
||||||
# elif note_text.endswith("+"):
|
# Set start time
|
||||||
# section_start_row = row
|
self._set_row_start_time(
|
||||||
# section_time = 0
|
row, self.current_track_start_time)
|
||||||
# # Set colour
|
# Set last played time to "Today"
|
||||||
# note_colour = NoteColours.get_colour(session, note_text)
|
self.item(row, self.COL_LAST_PLAYED).setText("Today")
|
||||||
# if not note_colour:
|
# Calculate next_start_time
|
||||||
# note_colour = Config.COLOUR_NOTES_PLAYLIST
|
next_start_time = self._calculate_end_time(
|
||||||
# self._set_row_colour(
|
self.current_track_start_time, track.duration)
|
||||||
# row, QColor(note_colour)
|
# Set end time
|
||||||
# )
|
self._set_row_end_time(row, next_start_time)
|
||||||
# # Notes are always bold
|
# Set colour
|
||||||
# self._set_row_bold(row)
|
self._set_row_colour(row, QColor(
|
||||||
# continue
|
Config.COLOUR_CURRENT_PLAYLIST))
|
||||||
#
|
# Make bold
|
||||||
# # Render unplayable tracks in correct colour
|
self._set_row_bold(row)
|
||||||
# if row in unreadable:
|
continue
|
||||||
# self._set_row_colour(
|
|
||||||
# row, QColor(Config.COLOUR_UNREADABLE)
|
# Render next track
|
||||||
# )
|
if row == next_row:
|
||||||
# self._set_row_bold(row)
|
# Set start time
|
||||||
# continue
|
# if there's a track playing, set start time from that
|
||||||
#
|
if current_row is not None:
|
||||||
# # Current row is a track row
|
start_time = self._calculate_end_time(
|
||||||
# track = self._get_row_track_object(row, session)
|
self.current_track_start_time, track.duration)
|
||||||
# # Add track time to section time if in timed section
|
else:
|
||||||
# if section_start_row is not None:
|
# No current track to base from, but don't change
|
||||||
# section_time += track.duration
|
# time if it's already set
|
||||||
# # Render current track
|
start_time = self._get_row_start_time(row)
|
||||||
# if filter_text:
|
if not start_time:
|
||||||
# try:
|
start_time = next_start_time
|
||||||
# if (track.title
|
self._set_row_start_time(row, start_time)
|
||||||
# and filter_text not in track.title.lower()
|
# Calculate next_start_time
|
||||||
# and track.artist
|
next_start_time = self._calculate_end_time(start_time,
|
||||||
# and filter_text not in track.artist.lower()):
|
track.duration)
|
||||||
# self.hideRow(row)
|
# Set end time
|
||||||
# continue
|
self._set_row_end_time(row, next_start_time)
|
||||||
# else:
|
# Set colour
|
||||||
# self.showRow(row)
|
self._set_row_colour(
|
||||||
# except TypeError:
|
row, QColor(Config.COLOUR_NEXT_PLAYLIST))
|
||||||
# print(f"TypeError: {track=}")
|
# Make bold
|
||||||
# else:
|
self._set_row_bold(row)
|
||||||
# self.showRow(row)
|
continue
|
||||||
# if row == current_row:
|
|
||||||
# # Set start time
|
# This is a track row other than next or current
|
||||||
# self._set_row_start_time(
|
if row in played:
|
||||||
# row, self.current_track_start_time)
|
# Played today, so update last played column
|
||||||
#
|
last_playedtime = track.lastplayed
|
||||||
# # Set last played time
|
last_played_str = get_relative_date(last_playedtime)
|
||||||
# last_played_str = get_relative_date(
|
self.item(row, self.COL_LAST_PLAYED).setText(
|
||||||
# self.current_track_start_time)
|
last_played_str)
|
||||||
# self.item(row, self.COL_LAST_PLAYED).setText(
|
if self.musicmuster.hide_played_tracks:
|
||||||
# last_played_str)
|
self.hideRow(row)
|
||||||
#
|
else:
|
||||||
# # Calculate next_start_time
|
self._set_row_not_bold(row)
|
||||||
# next_start_time = self._calculate_row_end_time(
|
else:
|
||||||
# row, self.current_track_start_time)
|
# Set start/end times as we haven't played it yet
|
||||||
#
|
if next_start_time:
|
||||||
# # Set end time
|
self._set_row_start_time(row, next_start_time)
|
||||||
# self._set_row_end_time(row, next_start_time)
|
next_start_time = self._calculate_end_time(
|
||||||
#
|
next_start_time, track.duration)
|
||||||
# # Set colour
|
# Set end time
|
||||||
# self._set_row_colour(row, QColor(
|
self._set_row_end_time(row, next_start_time)
|
||||||
# Config.COLOUR_CURRENT_PLAYLIST))
|
else:
|
||||||
#
|
# Clear start and end time
|
||||||
# # Make bold
|
self._set_row_start_time(row, None)
|
||||||
# self._set_row_bold(row)
|
self._set_row_end_time(row, None)
|
||||||
# continue
|
# Don't dim unplayed tracks
|
||||||
#
|
self._set_row_bold(row)
|
||||||
# # Render next track
|
# Stripe rows
|
||||||
# if row == next_row:
|
if row % 2:
|
||||||
# # if there's a track playing, set start time from that
|
self._set_row_colour(
|
||||||
# if current_row is not None:
|
row, QColor(Config.COLOUR_ODD_PLAYLIST))
|
||||||
# start_time = self._calculate_row_end_time(
|
else:
|
||||||
# current_row, self.current_track_start_time)
|
self._set_row_colour(
|
||||||
# else:
|
row, QColor(Config.COLOUR_EVEN_PLAYLIST))
|
||||||
# # No current track to base from, but don't change
|
|
||||||
# # time if it's already set
|
continue
|
||||||
# start_time = self._get_row_start_time(row)
|
|
||||||
# if not start_time:
|
# No track associated, so this row is a section header
|
||||||
# start_time = next_start_time
|
# Extract note text from database to ignore section timings
|
||||||
# self._set_row_start_time(row, start_time)
|
playlist_row = session.get(PlaylistTracks,
|
||||||
#
|
self._get_playlistrow_id(row))
|
||||||
# # Set end time
|
note_text = playlist_row.note
|
||||||
# next_start_time = self._calculate_row_end_time(row, start_time)
|
if filter_text:
|
||||||
# self._set_row_end_time(row, next_start_time)
|
if filter_text not in note_text.lower():
|
||||||
#
|
self.hideRow(row)
|
||||||
# # Set colour
|
continue
|
||||||
# self._set_row_colour(
|
else:
|
||||||
# row, QColor(Config.COLOUR_NEXT_PLAYLIST))
|
self.showRow(row)
|
||||||
#
|
else:
|
||||||
# # Make bold
|
self.showRow(row)
|
||||||
# self._set_row_bold(row)
|
# Does the note have a start time?
|
||||||
#
|
row_time = self._get_note_text_time(note_text)
|
||||||
# else:
|
if row_time:
|
||||||
# # This is a track row other than next or current
|
next_start_time = row_time
|
||||||
# if row in played:
|
# Does it delimit a section?
|
||||||
# # Played today, so update last played column
|
if section_start_row is not None:
|
||||||
# last_playedtime = track.lastplayed
|
if note_text.endswith("-"):
|
||||||
# last_played_str = get_relative_date(last_playedtime)
|
self._set_timed_section(session, section_start_row,
|
||||||
# self.item(row, self.COL_LAST_PLAYED).setText(
|
section_time)
|
||||||
# last_played_str)
|
section_start_row = None
|
||||||
# if hide_played:
|
section_time = 0
|
||||||
# self.hideRow(row)
|
elif note_text.endswith("+"):
|
||||||
# else:
|
section_start_row = row
|
||||||
# self._set_row_not_bold(row)
|
section_time = 0
|
||||||
# else:
|
# Set colour
|
||||||
# # Set start/end times as we haven't played it yet
|
note_colour = NoteColours.get_colour(session, note_text)
|
||||||
# if next_start_time:
|
if not note_colour:
|
||||||
# self._set_row_start_time(row, next_start_time)
|
note_colour = Config.COLOUR_NOTES_PLAYLIST
|
||||||
# next_start_time = self._calculate_row_end_time(
|
self._set_row_colour(
|
||||||
# row, next_start_time)
|
row, QColor(note_colour)
|
||||||
# # Set end time
|
)
|
||||||
# self._set_row_end_time(row, next_start_time)
|
# Notes are always bold
|
||||||
# else:
|
self._set_row_bold(row)
|
||||||
# # Clear start and end time
|
continue
|
||||||
# self._set_row_start_time(row, None)
|
|
||||||
# self._set_row_end_time(row, None)
|
# Have we had a section start but not end?
|
||||||
# # Don't dim unplayed tracks
|
if section_start_row is not None:
|
||||||
# self._set_row_bold(row)
|
self._set_timed_section(
|
||||||
# # Stripe rows
|
session, section_start_row, section_time, no_end=True)
|
||||||
# if row % 2:
|
|
||||||
# self._set_row_colour(
|
|
||||||
# row, QColor(Config.COLOUR_ODD_PLAYLIST))
|
|
||||||
# else:
|
|
||||||
# self._set_row_colour(
|
|
||||||
# row, QColor(Config.COLOUR_EVEN_PLAYLIST))
|
|
||||||
#
|
|
||||||
# # Have we had a section start but not end?
|
|
||||||
# if section_start_row is not None:
|
|
||||||
# self._set_timed_section(
|
|
||||||
# session, section_start_row, section_time, no_end=True)
|
|
||||||
#
|
#
|
||||||
# # ########## Internally called functions ##########
|
# # ########## Internally called functions ##########
|
||||||
#
|
#
|
||||||
@ -1034,16 +1039,15 @@ class PlaylistTab(QTableWidget):
|
|||||||
# with Session() as session:
|
# with Session() as session:
|
||||||
# track: Tracks = self._get_row_track_object(row, session)
|
# track: Tracks = self._get_row_track_object(row, session)
|
||||||
# open_in_audacity(track.path)
|
# open_in_audacity(track.path)
|
||||||
#
|
|
||||||
# def _calculate_row_end_time(self, row, start: Optional[datetime]) \
|
def _calculate_end_time(self, start: Optional[datetime],
|
||||||
# -> Optional[datetime]:
|
duration: int) -> Optional[datetime]:
|
||||||
# """Return this row's end time given its start time"""
|
"""Return datetime 'duration' ms after 'start'"""
|
||||||
#
|
|
||||||
# if start is None:
|
if start is None:
|
||||||
# return None
|
return None
|
||||||
#
|
|
||||||
# duration = self._get_row_duration(row)
|
return start + timedelta(milliseconds=duration)
|
||||||
# return start + timedelta(milliseconds=duration)
|
|
||||||
#
|
#
|
||||||
# def _context_menu(self, pos): # review
|
# def _context_menu(self, pos): # review
|
||||||
#
|
#
|
||||||
@ -1239,25 +1243,28 @@ class PlaylistTab(QTableWidget):
|
|||||||
# if column in [self.COL_TITLE, self.COL_ARTIST]:
|
# if column in [self.COL_TITLE, self.COL_ARTIST]:
|
||||||
# self.editItem(item)
|
# self.editItem(item)
|
||||||
#
|
#
|
||||||
# @staticmethod
|
|
||||||
# def _file_is_readable(path: str) -> bool:
|
|
||||||
# """
|
|
||||||
# Returns True if track path is readable, else False
|
|
||||||
#
|
|
||||||
# vlc cannot read files with a colon in the path
|
|
||||||
# """
|
|
||||||
#
|
|
||||||
# if os.access(path, os.R_OK):
|
|
||||||
# if ':' not in path:
|
|
||||||
# return True
|
|
||||||
#
|
|
||||||
# return False
|
|
||||||
#
|
|
||||||
# def _get_notes_rows(self) -> List[int]:
|
# def _get_notes_rows(self) -> List[int]:
|
||||||
# """Return rows marked as notes, or None"""
|
# """Return rows marked as notes, or None"""
|
||||||
#
|
#
|
||||||
# return self._meta_search(RowMeta.NOTE, one=False)
|
# return self._meta_search(RowMeta.NOTE, one=False)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
def _get_playlistrow_id(self, row):
|
||||||
|
"""Return the playlistrow_id associated with this row"""
|
||||||
|
|
||||||
|
playlistrow_id = (self.item(row, columns['userdata'].idx)
|
||||||
|
.data(self.PLAYLISTROW_ID))
|
||||||
|
|
||||||
|
return playlistrow_id
|
||||||
|
|
||||||
|
def _get_row_track_id(self, row):
|
||||||
|
"""Return the track_id associated with this row or None"""
|
||||||
|
|
||||||
|
track_id = (self.item(row, columns['userdata'].idx)
|
||||||
|
.data(self.ROW_TRACK_ID))
|
||||||
|
|
||||||
|
return track_id
|
||||||
|
|
||||||
# def _find_next_track_row(self, starting_row: int = None) -> Optional[int]:
|
# def _find_next_track_row(self, starting_row: int = None) -> Optional[int]:
|
||||||
# """
|
# """
|
||||||
# Find next track to play. If a starting row is given, start there;
|
# Find next track to play. If a starting row is given, start there;
|
||||||
@ -1284,24 +1291,24 @@ class PlaylistTab(QTableWidget):
|
|||||||
# return row
|
# return row
|
||||||
#
|
#
|
||||||
# return None
|
# return None
|
||||||
#
|
|
||||||
# def _get_current_track_row(self) -> Optional[int]:
|
def _get_current_track_row(self) -> Optional[int]:
|
||||||
# """Return row marked as current, or None"""
|
"""Return row marked as current, or None"""
|
||||||
#
|
|
||||||
# row = self._meta_search(RowMeta.CURRENT)
|
row = self._meta_search(RowMeta.CURRENT)
|
||||||
# if len(row) > 0:
|
if len(row) > 0:
|
||||||
# return row[0]
|
return row[0]
|
||||||
# else:
|
else:
|
||||||
# return None
|
return None
|
||||||
#
|
|
||||||
# def _get_next_track_row(self) -> Optional[int]:
|
def _get_next_track_row(self) -> Optional[int]:
|
||||||
# """Return row marked as next, or None"""
|
"""Return row marked as next, or None"""
|
||||||
#
|
|
||||||
# row = self._meta_search(RowMeta.NEXT)
|
row = self._meta_search(RowMeta.NEXT)
|
||||||
# if len(row) > 0:
|
if len(row) > 0:
|
||||||
# return row[0]
|
return row[0]
|
||||||
# else:
|
else:
|
||||||
# return None
|
return None
|
||||||
#
|
#
|
||||||
# @staticmethod
|
# @staticmethod
|
||||||
# def _get_note_text_time(text: str) -> Optional[datetime]:
|
# def _get_note_text_time(text: str) -> Optional[datetime]:
|
||||||
@ -1316,6 +1323,11 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Config.NOTE_TIME_FORMAT)
|
# Config.NOTE_TIME_FORMAT)
|
||||||
# except ValueError:
|
# except ValueError:
|
||||||
# return None
|
# return None
|
||||||
|
|
||||||
|
def _get_played_track_rows(self) -> List[int]:
|
||||||
|
"""Return rows marked as played, or None"""
|
||||||
|
|
||||||
|
return self._meta_search(RowMeta.PLAYED, one=False)
|
||||||
#
|
#
|
||||||
# def _get_row_duration(self, row: int) -> int:
|
# def _get_row_duration(self, row: int) -> int:
|
||||||
# """Return duration associated with this row"""
|
# """Return duration associated with this row"""
|
||||||
@ -1349,11 +1361,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
# note = Notes.get_by_id(session, note_id)
|
# note = Notes.get_by_id(session, note_id)
|
||||||
# return note
|
# return note
|
||||||
#
|
#
|
||||||
# def _get_played_track_rows(self) -> List[int]:
|
|
||||||
# """Return rows marked as played, or None"""
|
|
||||||
#
|
|
||||||
# return self._meta_search(RowMeta.PLAYED, one=False)
|
|
||||||
#
|
|
||||||
# def _get_unplayed_track_rows(self) -> Optional[List[int]]:
|
# def _get_unplayed_track_rows(self) -> Optional[List[int]]:
|
||||||
# """Return rows marked as unplayed, or None"""
|
# """Return rows marked as unplayed, or None"""
|
||||||
#
|
#
|
||||||
@ -1386,11 +1393,11 @@ class PlaylistTab(QTableWidget):
|
|||||||
# """Return rows marked as tracks, or None"""
|
# """Return rows marked as tracks, or None"""
|
||||||
#
|
#
|
||||||
# return self._meta_notset(RowMeta.NOTE)
|
# return self._meta_notset(RowMeta.NOTE)
|
||||||
#
|
|
||||||
# def _get_unreadable_track_rows(self) -> List[int]:
|
def _get_unreadable_track_rows(self) -> List[int]:
|
||||||
# """Return rows marked as unreadable, or None"""
|
"""Return rows marked as unreadable, or None"""
|
||||||
#
|
|
||||||
# return self._meta_search(RowMeta.UNREADABLE, one=False)
|
return self._meta_search(RowMeta.UNREADABLE, one=False)
|
||||||
#
|
#
|
||||||
# def _info_row(self, row: int) -> None:
|
# def _info_row(self, row: int) -> None:
|
||||||
# """Display popup with info re row"""
|
# """Display popup with info re row"""
|
||||||
@ -1502,11 +1509,12 @@ class PlaylistTab(QTableWidget):
|
|||||||
# next_row: Optional[int] = self._get_next_track_row()
|
# next_row: Optional[int] = self._get_next_track_row()
|
||||||
# if next_row is not None:
|
# if next_row is not None:
|
||||||
# self._meta_clear_attribute(next_row, RowMeta.NEXT)
|
# self._meta_clear_attribute(next_row, RowMeta.NEXT)
|
||||||
#
|
|
||||||
# def _meta_get(self, row: int) -> int:
|
def _meta_get(self, row: int) -> int:
|
||||||
# """Return row metadata"""
|
"""Return row metadata"""
|
||||||
#
|
|
||||||
# return self.item(row, self.COL_USERDATA).data(self.ROW_METADATA)
|
return (self.item(row, columns['userdata'].idx)
|
||||||
|
.data(self.ROW_METADATA))
|
||||||
#
|
#
|
||||||
# def _meta_notset(self, metadata: int) -> List[int]:
|
# def _meta_notset(self, metadata: int) -> List[int]:
|
||||||
# """
|
# """
|
||||||
@ -1523,34 +1531,34 @@ class PlaylistTab(QTableWidget):
|
|||||||
# matches.append(row)
|
# matches.append(row)
|
||||||
#
|
#
|
||||||
# return matches
|
# return matches
|
||||||
#
|
|
||||||
# def _meta_search(self, metadata: int, one: bool = True) -> List[int]:
|
def _meta_search(self, metadata: int, one: bool = True) -> List[int]:
|
||||||
# """
|
"""
|
||||||
# Search rows for metadata.
|
Search rows for metadata.
|
||||||
#
|
|
||||||
# If one is True, check that only one row matches and return
|
If one is True, check that only one row matches and return
|
||||||
# the row number.
|
the row number.
|
||||||
#
|
|
||||||
# If one is False, return a list of matching row numbers.
|
If one is False, return a list of matching row numbers.
|
||||||
# """
|
"""
|
||||||
#
|
|
||||||
# matches = []
|
matches = []
|
||||||
# for row in range(self.rowCount()):
|
for row in range(self.rowCount()):
|
||||||
# if self._meta_get(row):
|
if self._meta_get(row):
|
||||||
# if self._meta_get(row) & (1 << metadata):
|
if self._meta_get(row) & (1 << metadata):
|
||||||
# matches.append(row)
|
matches.append(row)
|
||||||
#
|
|
||||||
# if not one:
|
if not one:
|
||||||
# return matches
|
return matches
|
||||||
#
|
|
||||||
# if len(matches) <= 1:
|
if len(matches) <= 1:
|
||||||
# return matches
|
return matches
|
||||||
# else:
|
else:
|
||||||
# log.error(
|
log.error(
|
||||||
# f"Multiple matches for metadata '{metadata}' found "
|
f"Multiple matches for metadata '{metadata}' found "
|
||||||
# f"in rows: {', '.join([str(x) for x in matches])}"
|
f"in rows: {', '.join([str(x) for x in matches])}"
|
||||||
# )
|
)
|
||||||
# raise AttributeError(f"Multiple '{metadata}' metadata {matches}")
|
raise AttributeError(f"Multiple '{metadata}' metadata {matches}")
|
||||||
#
|
#
|
||||||
# def _meta_set_attribute(self, row: int, attribute: int) -> None:
|
# def _meta_set_attribute(self, row: int, attribute: int) -> None:
|
||||||
# """Set row metadata"""
|
# """Set row metadata"""
|
||||||
@ -1622,12 +1630,12 @@ class PlaylistTab(QTableWidget):
|
|||||||
# """Mark this row as played"""
|
# """Mark this row as played"""
|
||||||
#
|
#
|
||||||
# self._meta_set_attribute(row, RowMeta.PLAYED)
|
# self._meta_set_attribute(row, RowMeta.PLAYED)
|
||||||
#
|
|
||||||
# def _set_unreadable_row(self, row: int) -> None:
|
def _set_unreadable_row(self, row: int) -> None:
|
||||||
# """Mark this row as unreadable"""
|
"""Mark this row as unreadable"""
|
||||||
#
|
|
||||||
# self._meta_set_attribute(row, RowMeta.UNREADABLE)
|
self._meta_set_attribute(row, RowMeta.UNREADABLE)
|
||||||
#
|
|
||||||
# def _select_event(self) -> None:
|
# def _select_event(self) -> None:
|
||||||
# """
|
# """
|
||||||
# Called when item selection changes.
|
# Called when item selection changes.
|
||||||
@ -1726,27 +1734,26 @@ class PlaylistTab(QTableWidget):
|
|||||||
#
|
#
|
||||||
# # Notify musicmuster
|
# # Notify musicmuster
|
||||||
# self.musicmuster.this_is_the_next_track(self, track, session)
|
# self.musicmuster.this_is_the_next_track(self, track, session)
|
||||||
#
|
|
||||||
# def _set_row_bold(self, row: int, bold: bool = True) -> None:
|
def _set_row_bold(self, row: int, bold: bool = True) -> None:
|
||||||
# """Make row bold (bold=True) or not bold"""
|
"""Make row bold (bold=True) or not bold"""
|
||||||
#
|
|
||||||
# i: int
|
j: int
|
||||||
# j: int
|
|
||||||
#
|
boldfont: QFont = QFont()
|
||||||
# boldfont: QFont = QFont()
|
boldfont.setBold(bold)
|
||||||
# boldfont.setBold(bold)
|
for j in range(self.columnCount()):
|
||||||
# for j in range(self.columnCount()):
|
if self.item(row, j):
|
||||||
# if self.item(row, j):
|
self.item(row, j).setFont(boldfont)
|
||||||
# self.item(row, j).setFont(boldfont)
|
|
||||||
#
|
def _set_row_colour(self, row: int, colour: QColor) -> None:
|
||||||
# def _set_row_colour(self, row: int, colour: QColor) -> None:
|
"""Set row background colour"""
|
||||||
# """Set row background colour"""
|
|
||||||
#
|
j: int
|
||||||
# j: int
|
|
||||||
#
|
for j in range(2, self.columnCount()):
|
||||||
# for j in range(2, self.columnCount()):
|
if self.item(row, j):
|
||||||
# if self.item(row, j):
|
self.item(row, j).setBackground(colour)
|
||||||
# self.item(row, j).setBackground(colour)
|
|
||||||
#
|
#
|
||||||
# def _set_row_content(self, row: int, object_id: int) -> None:
|
# def _set_row_content(self, row: int, object_id: int) -> None:
|
||||||
# """Set content associated with this row"""
|
# """Set content associated with this row"""
|
||||||
@ -1756,37 +1763,37 @@ class PlaylistTab(QTableWidget):
|
|||||||
# self.item(row, self.COL_USERDATA).setData(
|
# self.item(row, self.COL_USERDATA).setData(
|
||||||
# self.CONTENT_OBJECT, object_id)
|
# self.CONTENT_OBJECT, object_id)
|
||||||
#
|
#
|
||||||
# def _set_row_duration(self, row: int, ms: int) -> None:
|
# def _set_row_duration(self, row: int, ms: int) -> None:
|
||||||
# """Set duration of this row in milliseconds"""
|
# """Set duration of this row in row metadata"""
|
||||||
#
|
#
|
||||||
# assert self.item(row, self.COL_USERDATA)
|
# assert self.item(row, columns['userdata'].idx)
|
||||||
#
|
#
|
||||||
# self.item(row, self.COL_USERDATA).setData(self.ROW_DURATION, ms)
|
# self.item(row, columns['userdata'].idx).setData(self.ROW_DURATION, ms)
|
||||||
#
|
|
||||||
# def _set_row_end_time(self, row: int, time: Optional[datetime]) -> None:
|
def _set_row_end_time(self, row: int, time: Optional[datetime]) -> None:
|
||||||
# """Set passed row end time to passed time"""
|
"""Set passed row end time to passed time"""
|
||||||
#
|
|
||||||
# try:
|
try:
|
||||||
# time_str: str = time.strftime(Config.TRACK_TIME_FORMAT)
|
time_str: str = time.strftime(Config.TRACK_TIME_FORMAT)
|
||||||
# except AttributeError:
|
except AttributeError:
|
||||||
# time_str = ""
|
time_str = ""
|
||||||
# item = QTableWidgetItem(time_str)
|
item = QTableWidgetItem(time_str)
|
||||||
# self.setItem(row, self.COL_END_TIME, item)
|
self.setItem(row, columns['end_time'].idx, item)
|
||||||
#
|
|
||||||
# def _set_row_not_bold(self, row: int) -> None:
|
def _set_row_not_bold(self, row: int) -> None:
|
||||||
# """Set row to not be bold"""
|
"""Set row to not be bold"""
|
||||||
#
|
|
||||||
# self._set_row_bold(row, False)
|
self._set_row_bold(row, False)
|
||||||
#
|
|
||||||
# def _set_row_start_time(self, row: int, time: Optional[datetime]) -> None:
|
def _set_row_start_time(self, row: int, time: Optional[datetime]) -> None:
|
||||||
# """Set passed row start time to passed time"""
|
"""Set passed row start time to passed time"""
|
||||||
#
|
|
||||||
# try:
|
try:
|
||||||
# time_str: str = time.strftime(Config.TRACK_TIME_FORMAT)
|
time_str: str = time.strftime(Config.TRACK_TIME_FORMAT)
|
||||||
# except AttributeError:
|
except AttributeError:
|
||||||
# time_str = ""
|
time_str = ""
|
||||||
# item: QTableWidgetItem = QTableWidgetItem(time_str)
|
item: QTableWidgetItem = QTableWidgetItem(time_str)
|
||||||
# self.setItem(row, self.COL_START_TIME, item)
|
self.setItem(row, columns['start_time'].idx, item)
|
||||||
#
|
#
|
||||||
# def _set_timed_section(self, session, start_row, ms, no_end=False):
|
# def _set_timed_section(self, session, start_row, ms, no_end=False):
|
||||||
# """Add duration to a marked section"""
|
# """Add duration to a marked section"""
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user