fixed readme + id3 timestamps
This commit is contained in:
parent
8a7335c741
commit
54a4be29ea
@ -24,6 +24,11 @@ pip install music-kraken
|
|||||||
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
|
### Notes for WSL
|
||||||
|
|
||||||
If you choose to run it in WSL, make sure ` ~/.local/bin` is added to your `$PATH` [#2][i2]
|
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.
|
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
|
[i2]: https://github.com/HeIIow2/music-downloader/issues/2
|
||||||
[mb]: https://musicbrainz.org/
|
[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 music_kraken.database.new_database as db
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import pycountry
|
||||||
|
|
||||||
|
|
||||||
def div(msg: str = ""):
|
def div(msg: str = ""):
|
||||||
print("-" * 50 + msg + "-" * 50)
|
print("-" * 50 + msg + "-" * 50)
|
||||||
@ -39,7 +42,10 @@ feature_artist = Artist(
|
|||||||
)
|
)
|
||||||
|
|
||||||
album_input = Album(
|
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 = [
|
album_input.artists = [
|
||||||
main_artist,
|
main_artist,
|
||||||
@ -62,7 +68,7 @@ song_input = Song(
|
|||||||
],
|
],
|
||||||
album=album_input,
|
album=album_input,
|
||||||
main_artist_list=[main_artist],
|
main_artist_list=[main_artist],
|
||||||
feature_artist_list=[feature_artist]
|
feature_artist_list=[feature_artist],
|
||||||
)
|
)
|
||||||
|
|
||||||
other_song = Song(
|
other_song = Song(
|
||||||
|
@ -3,6 +3,8 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
from pkg_resources import resource_string
|
from pkg_resources import resource_string
|
||||||
|
import datetime
|
||||||
|
import pycountry
|
||||||
|
|
||||||
from .objects.parents import Reference
|
from .objects.parents import Reference
|
||||||
from .objects.source import Source
|
from .objects.source import Source
|
||||||
@ -40,12 +42,12 @@ FROM Lyrics
|
|||||||
WHERE {where};
|
WHERE {where};
|
||||||
"""
|
"""
|
||||||
ALBUM_QUERY_UNJOINED = """
|
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
|
FROM Album
|
||||||
WHERE {where};
|
WHERE {where};
|
||||||
"""
|
"""
|
||||||
ALBUM_QUERY_JOINED = """
|
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
|
FROM Song
|
||||||
INNER JOIN Album a ON Song.album_id=a.id
|
INNER JOIN Album a ON Song.album_id=a.id
|
||||||
WHERE {where};
|
WHERE {where};
|
||||||
@ -131,16 +133,15 @@ class Database:
|
|||||||
|
|
||||||
def push_album(self, album: Album):
|
def push_album(self, album: Album):
|
||||||
table = "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 = (
|
values = (
|
||||||
album.id,
|
album.id,
|
||||||
album.title,
|
album.title,
|
||||||
album.copyright,
|
album.label,
|
||||||
album.album_status,
|
album.album_status,
|
||||||
album.language,
|
album.iso_639_2_language,
|
||||||
album.year,
|
album.date.strftime("%Y-%m-%d"),
|
||||||
album.date,
|
|
||||||
album.country,
|
album.country,
|
||||||
album.barcode,
|
album.barcode,
|
||||||
album.albumsort,
|
album.albumsort,
|
||||||
@ -541,11 +542,10 @@ class Database:
|
|||||||
album_obj = Album(
|
album_obj = Album(
|
||||||
id_=album_id,
|
id_=album_id,
|
||||||
title=album_result['title'],
|
title=album_result['title'],
|
||||||
copyright_=album_result['copyright'],
|
label=album_result['label'],
|
||||||
album_status=album_result['album_status'],
|
album_status=album_result['album_status'],
|
||||||
language=album_result['language'],
|
language=pycountry.languages.get(alpha_3=album_result['language']),
|
||||||
year=album_result['year'],
|
date=datetime.datetime.strptime(album_result['date'], "%Y-%m-%d").date(),
|
||||||
date=album_result['date'],
|
|
||||||
country=album_result['country'],
|
country=album_result['country'],
|
||||||
barcode=album_result['barcode'],
|
barcode=album_result['barcode'],
|
||||||
is_split=album_result['is_split'],
|
is_split=album_result['is_split'],
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List, Dict, Tuple
|
from typing import List, Dict, Tuple
|
||||||
from mutagen import id3
|
from mutagen import id3
|
||||||
|
import datetime
|
||||||
|
|
||||||
from .parents import (
|
from .parents import (
|
||||||
ID3Metadata
|
ID3Metadata
|
||||||
@ -10,6 +11,9 @@ from .parents import (
|
|||||||
class Mapping(Enum):
|
class Mapping(Enum):
|
||||||
"""
|
"""
|
||||||
These frames belong to the id3 standart
|
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
|
# Textframes
|
||||||
TITLE = "TIT2"
|
TITLE = "TIT2"
|
||||||
@ -37,7 +41,7 @@ class Mapping(Enum):
|
|||||||
LYRICIST = "TEXT"
|
LYRICIST = "TEXT"
|
||||||
WRITER = "TEXT"
|
WRITER = "TEXT"
|
||||||
ARTIST = "TPE1"
|
ARTIST = "TPE1"
|
||||||
LANGUAGE = "TLAN"
|
LANGUAGE = "TLAN" # https://en.wikipedia.org/wiki/ISO_639-2
|
||||||
ITUNESCOMPILATION = "TCMP"
|
ITUNESCOMPILATION = "TCMP"
|
||||||
REMIXED_BY = "TPE4"
|
REMIXED_BY = "TPE4"
|
||||||
RADIO_STATION_OWNER = "TRSO"
|
RADIO_STATION_OWNER = "TRSO"
|
||||||
@ -58,6 +62,7 @@ class Mapping(Enum):
|
|||||||
ALBUM = "TALB"
|
ALBUM = "TALB"
|
||||||
ALBUMSORTORDER = "TSOA"
|
ALBUMSORTORDER = "TSOA"
|
||||||
ALBUMARTISTSORTORDER = "TSO2"
|
ALBUMARTISTSORTORDER = "TSO2"
|
||||||
|
TAGGING_TIME = "TDTG"
|
||||||
|
|
||||||
SOURCE_WEBPAGE_URL = "WOAS"
|
SOURCE_WEBPAGE_URL = "WOAS"
|
||||||
FILE_WEBPAGE_URL = "WOAF"
|
FILE_WEBPAGE_URL = "WOAF"
|
||||||
@ -93,6 +98,91 @@ class Mapping(Enum):
|
|||||||
return cls.get_url_instance(key, value)
|
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:
|
class Metadata:
|
||||||
"""
|
"""
|
||||||
Shall only be read or edited via the Song object.
|
Shall only be read or edited via the Song object.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from typing import List, Tuple, Dict
|
from typing import List, Tuple, Dict
|
||||||
from mutagen.easyid3 import EasyID3
|
import datetime
|
||||||
|
import pycountry
|
||||||
|
|
||||||
from .metadata import (
|
from .metadata import (
|
||||||
Mapping as ID3_MAPPING,
|
Mapping as ID3_MAPPING,
|
||||||
@ -278,11 +279,10 @@ class Album(DatabaseObject, ID3Metadata):
|
|||||||
self,
|
self,
|
||||||
id_: str = None,
|
id_: str = None,
|
||||||
title: str = None,
|
title: str = None,
|
||||||
copyright_: str = None,
|
label: str = None,
|
||||||
album_status: str = None,
|
album_status: str = None,
|
||||||
language: str = None,
|
language: pycountry.Languages = None,
|
||||||
year: str = None,
|
date: datetime.date = None,
|
||||||
date: str = None,
|
|
||||||
country: str = None,
|
country: str = None,
|
||||||
barcode: str = None,
|
barcode: str = None,
|
||||||
is_split: bool = False,
|
is_split: bool = False,
|
||||||
@ -291,20 +291,10 @@ class Album(DatabaseObject, ID3Metadata):
|
|||||||
) -> None:
|
) -> None:
|
||||||
DatabaseObject.__init__(self, id_=id_, dynamic=dynamic)
|
DatabaseObject.__init__(self, id_=id_, dynamic=dynamic)
|
||||||
self.title: str = title
|
self.title: str = title
|
||||||
self.copyright: str = copyright_
|
|
||||||
self.album_status: str = album_status
|
self.album_status: str = album_status
|
||||||
"""
|
self.label = label
|
||||||
TODO
|
self.language: pycountry.Languages = language
|
||||||
MAKE SURE THIS IS IN THE CORRECT FORMAT
|
self.date: datetime.date = date
|
||||||
"""
|
|
||||||
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.country: str = country
|
self.country: str = country
|
||||||
"""
|
"""
|
||||||
TODO
|
TODO
|
||||||
@ -344,10 +334,25 @@ class Album(DatabaseObject, ID3Metadata):
|
|||||||
return {
|
return {
|
||||||
ID3_MAPPING.ALBUM: [self.title],
|
ID3_MAPPING.ALBUM: [self.title],
|
||||||
ID3_MAPPING.COPYRIGHT: [self.copyright],
|
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]
|
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,
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
title TEXT,
|
title TEXT,
|
||||||
copyright TEXT,
|
label TEXT,
|
||||||
album_status TEXT,
|
album_status TEXT,
|
||||||
language TEXT,
|
language TEXT,
|
||||||
year TEXT,
|
|
||||||
date TEXT,
|
date TEXT,
|
||||||
country TEXT,
|
country TEXT,
|
||||||
barcode TEXT,
|
barcode TEXT,
|
||||||
|
BIN
src/test.db
BIN
src/test.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user