Skip to content

Commit

Permalink
feat: added directory parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Dzheremi2 committed Dec 22, 2024
1 parent 11fea23 commit b14f13a
Show file tree
Hide file tree
Showing 17 changed files with 347 additions and 78 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
.idea/
COPYING
.flatpak/
.vscode/
builddir/
data/icons/icons/symbolic/lyrics-symbolic.svg
data/icons/icons/symbolic/publish-symbolic.svg
Expand Down
25 changes: 25 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/.hg/store/**": true,
"**/.dart_tool": true,
".flatpak/**": true,
"_build/**": true
},
"mesonbuild.configureOnOpen": false,
"mesonbuild.buildFolder": "_build",
"search.followSymlinks": false,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
},
"editor.rulers": [88],
},
"isort.args":["--profile", "black"],
"python.analysis.diagnosticSeverityOverrides": {
"reportMissingModuleSource": "none"
}
}
3 changes: 3 additions & 0 deletions chronograph/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@


class ChronographApplication(Adw.Application):
"""Application class"""

win: ChronographWindow

def __init__(self) -> None:
Expand All @@ -32,6 +34,7 @@ def do_activate(self) -> None: # pylint: disable=arguments-differ
("quit", ("<primary>q",)),
("toggle_sidebar", ("F9",), shared.win),
("toggle_search", ("<primary>f",), shared.win),
("select_dir", ("<primary><shift>o",), shared.win),
}
)

Expand Down
16 changes: 16 additions & 0 deletions chronograph/shared.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pathlib import Path

from gi.repository import Gio, Adw # type: ignore

APP_ID: str
VERSION: str
PREFIX: str

config_dir: Path
cache_dir: Path

schema: Gio.Settings
state_schema: Gio.Settings

app: Adw.Application
win: Adw.ApplicationWindow
40 changes: 40 additions & 0 deletions chronograph/ui/BoxDialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from gi.repository import Adw, Gtk # type: ignore

from chronograph import shared


@Gtk.Template(resource_path=shared.PREFIX + "/gtk/ui/BoxDialog.ui")
class BoxDialog(Adw.Dialog):
"""Dialog with lines of `Adw.ActionRow(s)` with provided content
Parameters
----------
label : str
Label of the dialog
lines_content : tuple
titles and subtitles of `Adw.ActionRow(s)`. Like `(("1st Title", "1st subtitle"), ("2nd title", "2nd subtitle"), ...)`
GTK Objects
----------
::
diaglog_title_label : Gtk.Label -> Label of the dialog
props_list : Gtk.ListBox -> ListBox with `Adw.ActionRow(s)` with provided data
"""

__gtype_name__ = "BoxDialog"

dialog_title_label: Gtk.Label = Gtk.Template.Child()
props_list: Gtk.ListBox = Gtk.Template.Child()

def __init__(self, label: str, lines_content: tuple) -> None:
super().__init__()

for entry in lines_content:
self.props_list.append(
Adw.ActionRow(
title=entry[0], subtitle=entry[1], css_classes=["property"]
)
)

self.dialog_title_label.set_label(label)
21 changes: 21 additions & 0 deletions chronograph/ui/SongCard.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@

@Gtk.Template(resource_path=shared.PREFIX + "/gtk/ui/SongCard.ui")
class SongCard(Gtk.Box):
"""Card with Title, Artist and Cover of provided file
Parameters
----------
file : Union[FileID3, FileVorbis]
File of `.ogg`, `.flac`, `.mp3` and `.wav` formats
GTK Objects
----------
::
buttons_revealer: Gtk.Revealer -> Revealer for Play and Edit buttons
play_button: Gtk.Button -> Play button
metadata_editor_button: Gtk.Button -> Metadata editor button
cover_button: Gtk.Button -> Clickable cover of song
cover: Gtk.Image -> Cover image of song
title_label: Gtk.Label -> Title of song
artist_label: Gtk.Label -> Artist of song
"""

__gtype_name__ = "SongCard"

buttons_revealer: Gtk.Revealer = Gtk.Template.Child()
Expand All @@ -35,6 +55,7 @@ def __init__(self, file: Union[FileID3, FileVorbis]) -> None:
self.cover.props.paintable = _texture

def toggle_buttons(self, *_args) -> None:
"""Sets if buttons should be visible or not"""
self.buttons_revealer.set_reveal_child(
not self.buttons_revealer.get_reveal_child()
)
36 changes: 22 additions & 14 deletions chronograph/utils/file.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
from typing import Any, Union
from typing import Union

import mutagen
from gi.repository import Gdk, GLib # type: ignore


class BaseFile:
"""A base class for mutagen filetypes classes"""
"""A base class for mutagen filetypes classes
Parameters
----------
path : str
A path to file for loading
Props
--------
::
title : str -> Title of song
artist : str -> Artist of song
album : str -> Album of song
cover : Gdk.Texture | str -> Cover of song
"""

__gtype_name__ = "BaseFile"

_title: str = None
_artist: str = None
_album: str = None
_cover: Any = None
_mutagen_file = None
_title: str = "Unknown"
_artist: str = "Unknown"
_album: str = "Unknown"
_cover: Union[Gdk.Texture, str] = None
_mutagen_file: dict = None

def __init__(self, path: str) -> None:
"""
Parameters
----------
path : str
A path to file for loading
"""

self._path: str = path
self.load_from_file(path)

Expand Down
45 changes: 26 additions & 19 deletions chronograph/utils/file_mutagen_id3.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,43 @@


class FileID3(BaseFile):
"""A ID3 compatible file class. Inherited from `BaseFile`
Parameters
--------
path : str
A path to file for loading
"""

__gtype_name__ = "FileID3"

def __init__(self, path):
def __init__(self, path) -> None:
super().__init__(path)
self.load_cover()
self.load_str_data()

# pylint: disable=attribute-defined-outside-init
def load_cover(self) -> None:
"""Extracts cover from song file. If no cover, then sets cover as 'icon'"""
pictures = self._mutagen_file.tags.getall("APIC")
if len(pictures) != 0:
self._cover = pictures[0].data
if len(pictures) == 0:
"""Extracts cover from song file. If no cover, then sets cover as `icon`"""
if self._mutagen_file.tags is not None:
pictures = self._mutagen_file.tags.getall("APIC")
if len(pictures) != 0:
self._cover = pictures[0].data
if len(pictures) == 0:
self._cover = "icon"
else:
self._cover = "icon"

def load_str_data(self) -> None:
"""Sets all string data from tags. If data is unavailable, then sets 'Unknown'
"""
if (_title := self._mutagen_file.tags["TIT2"].text[0]) is not None:
self._title = _title
else:
self._title = os.path.basename(self._path)
"""Sets all string data from tags. If data is unavailable, then sets `Unknown`"""
if self._mutagen_file.tags is not None:
if (_title := self._mutagen_file.tags["TIT2"].text[0]) is not None:
self._title = _title

if (_artist := self._mutagen_file.tags["TPE1"].text[0]) is not None:
self._artist = _artist
else:
self._artist = "Unknown"
if (_artist := self._mutagen_file.tags["TPE1"].text[0]) is not None:
self._artist = _artist

if (_album := self._mutagen_file.tags["TALB"].text[0]) is not None:
self._album = _album
if (_album := self._mutagen_file.tags["TALB"].text[0]) is not None:
self._album = _album
else:
self._album = "Unknown"
self._title = os.path.basename(self._path)
43 changes: 27 additions & 16 deletions chronograph/utils/file_mutagen_vorbis.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import base64
import os

from mutagen.flac import FLAC, Picture
from mutagen.flac import error as FLACError
Expand All @@ -7,9 +8,17 @@


class FileVorbis(BaseFile):
"""A Vorbis (ogg, flac) compatible file class. Inherited from `BaseFile`
Parameters
--------
path : str
A path to file for loading
"""

__gtype_name__ = "FileVorbis"

def __init__(self, path):
def __init__(self, path) -> None:
super().__init__(path)

self.load_cover()
Expand All @@ -18,7 +27,7 @@ def __init__(self, path):
def load_cover(self) -> None:
"""Loads cover for Vorbis format audio"""
if isinstance(self._mutagen_file, FLAC) and self._mutagen_file.pictures:
if self._mutagen_file.pictures[0].data != None:
if self._mutagen_file.pictures[0].data is not None:
self._cover = self._mutagen_file.pictures[0].data
else:
self._cover = "icon"
Expand All @@ -40,7 +49,6 @@ def load_cover(self) -> None:
self._cover = "icon"
else:
self._cover = _data
print(_data)
else:
self._cover = "icon"

Expand All @@ -50,23 +58,26 @@ def load_str_data(self, tags: list = ["title", "artist", "album"]) -> None:
Parameters
----------
tags : list, persistent
list of tags for parsing in vorbis comment, by default ["title", "artist", "album"]
list of tags for parsing in vorbis comment, by default `["title", "artist", "album"]`
"""
for tag in tags:
try:
text = (
"Unknown"
if not self._mutagen_file.tags[tag.lower()][0]
else self._mutagen_file.tags[tag.lower()][0]
)
setattr(self, f"_{tag}", text)
except KeyError:
if self._mutagen_file.tags is not None:
for tag in tags:
try:
text = (
"Unknown"
if not self._mutagen_file.tags[tag.upper()][0]
else self._mutagen_file.tags[tag.upper()][0]
if not self._mutagen_file.tags[tag.lower()][0]
else self._mutagen_file.tags[tag.lower()][0]
)
setattr(self, f"_{tag}", text)
except KeyError:
setattr(self, f"_{tag}", "Unknown")
try:
text = (
"Unknown"
if not self._mutagen_file.tags[tag.upper()][0]
else self._mutagen_file.tags[tag.upper()][0]
)
setattr(self, f"_{tag}", text)
except KeyError:
setattr(self, f"_{tag}", "Unknown")
if self._title == "Unknown":
self._title = os.path.basename(self._path)
49 changes: 49 additions & 0 deletions chronograph/utils/parsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import os
from pathlib import Path
from typing import Union

from gi.repository import GLib # type: ignore

from chronograph import shared
from chronograph.ui.SongCard import SongCard
from chronograph.utils.file_mutagen_id3 import FileID3
from chronograph.utils.file_mutagen_vorbis import FileVorbis


def dir_parser(path: str, *_args) -> None:
"""Parses directory and creates `SongCard` instances for files in directory of formats `ogg`, `flac`, `mp3` and `wav`
Parameters
----------
path : str
Path to directory to parse
"""
shared.win.library.remove_all()
path = path + "/"
mutagen_files = []
for file in os.listdir(path):
if not os.path.isdir(path + file):
if Path(file).suffix in (".ogg", ".flac"):
mutagen_files.append(FileVorbis(path + file))
elif Path(file).suffix in (".mp3", ".wav"):
mutagen_files.append(FileID3(path + file))

for file in mutagen_files:
GLib.idle_add(songcard_idle, file)
shared.win.library_scrolled_window.set_child(shared.win.library)
# NOTE: This should be implemented in ALL parsers functions
# for child in shared.win.library:
# child.set_focusable(False)


def songcard_idle(file: Union[FileID3, FileVorbis]) -> None:
"""Appends new `SongCard` instance to `ChronographWindow.library`
Parameters
----------
file : Union[FileID3, FileVorbis]
File of song
"""
song_card = SongCard(file)
shared.win.library.append(song_card)
song_card.get_parent().set_focusable(False)
Loading

0 comments on commit b14f13a

Please sign in to comment.