fixed readme + id3 timestamps
This commit is contained in:
parent
8a7335c741
commit
54a4be29ea
@ -24,6 +24,11 @@ pip install music-kraken
|
||||
music-kraken
|
||||
```
|
||||
|
||||
### Notes for Python 3.9
|
||||
|
||||
Unfortunately I use features that newly git introduced in [Python 3.10](https://docs.python.org/3/library/types.html#types.UnionType).
|
||||
So unfortunately you **CAN'T** run this programm with python 3.9. [#10][i10]
|
||||
|
||||
### Notes for WSL
|
||||
|
||||
If you choose to run it in WSL, make sure ` ~/.local/bin` is added to your `$PATH` [#2][i2]
|
||||
@ -331,5 +336,6 @@ To get the Lyrics, I scrape them, and put those in the USLT ID3 Tags of for exam
|
||||
For the lyrics source the page [https://genius.com/](https://genius.com/) is easily sufficient. It has most songs. Some songs are not present though, but that is fine, because the lyrics are optional anyways.
|
||||
|
||||
|
||||
[i10]: https://github.com/HeIIow2/music-downloader/issues/10
|
||||
[i2]: https://github.com/HeIIow2/music-downloader/issues/2
|
||||
[mb]: https://musicbrainz.org/
|
||||
|
10
src/goof.py
10
src/goof.py
@ -16,6 +16,9 @@ from music_kraken.tagging import (
|
||||
|
||||
import music_kraken.database.new_database as db
|
||||
|
||||
import datetime
|
||||
import pycountry
|
||||
|
||||
|
||||
def div(msg: str = ""):
|
||||
print("-" * 50 + msg + "-" * 50)
|
||||
@ -39,7 +42,10 @@ feature_artist = Artist(
|
||||
)
|
||||
|
||||
album_input = Album(
|
||||
title="One Final Action"
|
||||
title="One Final Action",
|
||||
date=datetime.date(1986, 3, 1),
|
||||
language=pycountry.languages.get(alpha_2="en"),
|
||||
label="cum productions"
|
||||
)
|
||||
album_input.artists = [
|
||||
main_artist,
|
||||
@ -62,7 +68,7 @@ song_input = Song(
|
||||
],
|
||||
album=album_input,
|
||||
main_artist_list=[main_artist],
|
||||
feature_artist_list=[feature_artist]
|
||||
feature_artist_list=[feature_artist],
|
||||
)
|
||||
|
||||
other_song = Song(
|
||||
|
@ -3,6 +3,8 @@ import os
|
||||
import logging
|
||||
from typing import List, Tuple
|
||||
from pkg_resources import resource_string
|
||||
import datetime
|
||||
import pycountry
|
||||
|
||||
from .objects.parents import Reference
|
||||
from .objects.source import Source
|
||||
@ -40,12 +42,12 @@ FROM Lyrics
|
||||
WHERE {where};
|
||||
"""
|
||||
ALBUM_QUERY_UNJOINED = """
|
||||
SELECT Album.id AS album_id, title, copyright, album_status, language, year, date, country, barcode, albumsort, is_split
|
||||
SELECT Album.id AS album_id, title, label, album_status, language, date, country, barcode, albumsort, is_split
|
||||
FROM Album
|
||||
WHERE {where};
|
||||
"""
|
||||
ALBUM_QUERY_JOINED = """
|
||||
SELECT a.id AS album_id, a.title, a.copyright, a.album_status, a.language, a.year, a.date, a.country, a.barcode, a.albumsort, a.is_split
|
||||
SELECT a.id AS album_id, a.title, a.label, a.album_status, a.language, a.date, a.country, a.barcode, a.albumsort, a.is_split
|
||||
FROM Song
|
||||
INNER JOIN Album a ON Song.album_id=a.id
|
||||
WHERE {where};
|
||||
@ -131,16 +133,15 @@ class Database:
|
||||
|
||||
def push_album(self, album: Album):
|
||||
table = "Album"
|
||||
query = f"INSERT OR REPLACE INTO {table} (id, title, copyright, album_status, language, year, date, country, barcode, albumsort, is_split) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
|
||||
query = f"INSERT OR REPLACE INTO {table} (id, title, label, album_status, language, date, country, barcode, albumsort, is_split) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
|
||||
|
||||
values = (
|
||||
album.id,
|
||||
album.title,
|
||||
album.copyright,
|
||||
album.label,
|
||||
album.album_status,
|
||||
album.language,
|
||||
album.year,
|
||||
album.date,
|
||||
album.iso_639_2_language,
|
||||
album.date.strftime("%Y-%m-%d"),
|
||||
album.country,
|
||||
album.barcode,
|
||||
album.albumsort,
|
||||
@ -541,11 +542,10 @@ class Database:
|
||||
album_obj = Album(
|
||||
id_=album_id,
|
||||
title=album_result['title'],
|
||||
copyright_=album_result['copyright'],
|
||||
label=album_result['label'],
|
||||
album_status=album_result['album_status'],
|
||||
language=album_result['language'],
|
||||
year=album_result['year'],
|
||||
date=album_result['date'],
|
||||
language=pycountry.languages.get(alpha_3=album_result['language']),
|
||||
date=datetime.datetime.strptime(album_result['date'], "%Y-%m-%d").date(),
|
||||
country=album_result['country'],
|
||||
barcode=album_result['barcode'],
|
||||
is_split=album_result['is_split'],
|
||||
|
@ -1,6 +1,7 @@
|
||||
from enum import Enum
|
||||
from typing import List, Dict, Tuple
|
||||
from mutagen import id3
|
||||
import datetime
|
||||
|
||||
from .parents import (
|
||||
ID3Metadata
|
||||
@ -10,6 +11,9 @@ from .parents import (
|
||||
class Mapping(Enum):
|
||||
"""
|
||||
These frames belong to the id3 standart
|
||||
https://web.archive.org/web/20220830091059/https://id3.org/id3v2.4.0-frames
|
||||
https://id3lib.sourceforge.net/id3/id3v2com-00.html
|
||||
https://mutagen-specs.readthedocs.io/en/latest/id3/id3v2.4.0-frames.html
|
||||
"""
|
||||
# Textframes
|
||||
TITLE = "TIT2"
|
||||
@ -37,7 +41,7 @@ class Mapping(Enum):
|
||||
LYRICIST = "TEXT"
|
||||
WRITER = "TEXT"
|
||||
ARTIST = "TPE1"
|
||||
LANGUAGE = "TLAN"
|
||||
LANGUAGE = "TLAN" # https://en.wikipedia.org/wiki/ISO_639-2
|
||||
ITUNESCOMPILATION = "TCMP"
|
||||
REMIXED_BY = "TPE4"
|
||||
RADIO_STATION_OWNER = "TRSO"
|
||||
@ -58,6 +62,7 @@ class Mapping(Enum):
|
||||
ALBUM = "TALB"
|
||||
ALBUMSORTORDER = "TSOA"
|
||||
ALBUMARTISTSORTORDER = "TSO2"
|
||||
TAGGING_TIME = "TDTG"
|
||||
|
||||
SOURCE_WEBPAGE_URL = "WOAS"
|
||||
FILE_WEBPAGE_URL = "WOAF"
|
||||
@ -93,6 +98,91 @@ class Mapping(Enum):
|
||||
return cls.get_url_instance(key, value)
|
||||
|
||||
|
||||
class ID3Timestamp(datetime.datetime):
|
||||
def __init__(
|
||||
self,
|
||||
year: int = None,
|
||||
month: int = None,
|
||||
day: int = None,
|
||||
hour: int = None,
|
||||
minute: int = None,
|
||||
second: int = None,
|
||||
microsecond=0,
|
||||
tzinfo=None,
|
||||
*,
|
||||
fold=0
|
||||
):
|
||||
self.has_year = year is not None
|
||||
self.has_month = month is not None
|
||||
self.has_day = day is not None
|
||||
self.has_hour = hour is not None
|
||||
self.has_minute = minute is not None
|
||||
self.has_second = second is not None
|
||||
self.has_microsecond = microsecond is not None
|
||||
|
||||
if not self.has_year:
|
||||
year = 1
|
||||
if not self.has_month:
|
||||
month = 1
|
||||
if not self.has_day:
|
||||
day = 1
|
||||
super().__init__(
|
||||
year=year,
|
||||
month=month,
|
||||
day=day,
|
||||
hour=hour,
|
||||
minute=minute,
|
||||
second=second,
|
||||
microsecond=microsecond,
|
||||
tzinfo=tzinfo,
|
||||
fold=fold
|
||||
)
|
||||
|
||||
def get_timestamp(self) -> str:
|
||||
"""
|
||||
https://mutagen-specs.readthedocs.io/en/latest/id3/id3v2.4.0-structure.html
|
||||
|
||||
The timestamp fields are based on a subset of ISO 8601. When being as precise as possible the format of a
|
||||
time string is
|
||||
- yyyy-MM-ddTHH:mm:ss
|
||||
- (year[%Y], “-”, month[%m], “-”, day[%d], “T”, hour (out of 24)[%H], ”:”, minutes[%M], ”:”, seconds[%S])
|
||||
- %Y-%m-%dT%H:%M:%S
|
||||
but the precision may be reduced by removing as many time indicators as wanted. Hence valid timestamps are
|
||||
- yyyy
|
||||
- yyyy-MM
|
||||
- yyyy-MM-dd
|
||||
- yyyy-MM-ddTHH
|
||||
- yyyy-MM-ddTHH:mm
|
||||
- yyyy-MM-ddTHH:mm:ss
|
||||
All time stamps are UTC. For durations, use the slash character as described in 8601,
|
||||
and for multiple non-contiguous dates, use multiple strings, if allowed by the frame definition.
|
||||
|
||||
:return timestamp: as timestamp in the format of the id3 time as above described
|
||||
"""
|
||||
|
||||
if self.has_year and self.has_month and self.has_day and self.has_hour and self.has_minute and self.has_second:
|
||||
return self.strftime("%Y-%m-%dT%H:%M:%S")
|
||||
if self.has_year and self.has_month and self.has_day and self.has_hour and self.has_minute:
|
||||
return self.strftime("%Y-%m-%dT%H:%M")
|
||||
if self.has_year and self.has_month and self.has_day and self.has_hour:
|
||||
return self.strftime("%Y-%m-%dT%H")
|
||||
if self.has_year and self.has_month and self.has_day:
|
||||
return self.strftime("%Y-%m-%d")
|
||||
if self.has_year and self.has_month:
|
||||
return self.strftime("%Y-%m")
|
||||
if self.has_year:
|
||||
return self.strftime("%Y")
|
||||
return ""
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.timestamp
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.timestamp
|
||||
|
||||
timestamp: str = property(fget=get_timestamp)
|
||||
|
||||
|
||||
class Metadata:
|
||||
"""
|
||||
Shall only be read or edited via the Song object.
|
||||
|
@ -1,6 +1,7 @@
|
||||
import os
|
||||
from typing import List, Tuple, Dict
|
||||
from mutagen.easyid3 import EasyID3
|
||||
import datetime
|
||||
import pycountry
|
||||
|
||||
from .metadata import (
|
||||
Mapping as ID3_MAPPING,
|
||||
@ -278,11 +279,10 @@ class Album(DatabaseObject, ID3Metadata):
|
||||
self,
|
||||
id_: str = None,
|
||||
title: str = None,
|
||||
copyright_: str = None,
|
||||
label: str = None,
|
||||
album_status: str = None,
|
||||
language: str = None,
|
||||
year: str = None,
|
||||
date: str = None,
|
||||
language: pycountry.Languages = None,
|
||||
date: datetime.date = None,
|
||||
country: str = None,
|
||||
barcode: str = None,
|
||||
is_split: bool = False,
|
||||
@ -291,20 +291,10 @@ class Album(DatabaseObject, ID3Metadata):
|
||||
) -> None:
|
||||
DatabaseObject.__init__(self, id_=id_, dynamic=dynamic)
|
||||
self.title: str = title
|
||||
self.copyright: str = copyright_
|
||||
self.album_status: str = album_status
|
||||
"""
|
||||
TODO
|
||||
MAKE SURE THIS IS IN THE CORRECT FORMAT
|
||||
"""
|
||||
self.language: str = language
|
||||
"""
|
||||
TODO
|
||||
only store the date in a python date object and derive the
|
||||
year from it
|
||||
"""
|
||||
self.year: str = year
|
||||
self.date: str = date
|
||||
self.label = label
|
||||
self.language: pycountry.Languages = language
|
||||
self.date: datetime.date = date
|
||||
self.country: str = country
|
||||
"""
|
||||
TODO
|
||||
@ -344,10 +334,25 @@ class Album(DatabaseObject, ID3Metadata):
|
||||
return {
|
||||
ID3_MAPPING.ALBUM: [self.title],
|
||||
ID3_MAPPING.COPYRIGHT: [self.copyright],
|
||||
ID3_MAPPING.LANGUAGE: [self.language],
|
||||
ID3_MAPPING.LANGUAGE: [self.iso_639_2_language],
|
||||
ID3_MAPPING.ALBUM_ARTIST: [a.name for a in self.artists]
|
||||
}
|
||||
|
||||
def get_copyright(self) -> str:
|
||||
if self.date.year == 1 or self.label is None:
|
||||
return None
|
||||
|
||||
return f"{self.date.year} {self.label}"
|
||||
|
||||
def get_iso_639_2_lang(self) -> str:
|
||||
if self.language is None:
|
||||
return None
|
||||
|
||||
return self.language.alpha_3
|
||||
|
||||
copyright = property(fget=get_copyright)
|
||||
iso_639_2_language = property(fget=get_iso_639_2_lang)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
@ -26,10 +26,9 @@ CREATE TABLE Album
|
||||
(
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
title TEXT,
|
||||
copyright TEXT,
|
||||
label TEXT,
|
||||
album_status TEXT,
|
||||
language TEXT,
|
||||
year TEXT,
|
||||
date TEXT,
|
||||
country TEXT,
|
||||
barcode TEXT,
|
||||
|
BIN
src/test.db
BIN
src/test.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user