Skip to content

Commit 015d595

Browse files
authored
Merge pull request #59 from cjcodeproj/integration
Integration to Main (0.1.7)
2 parents 082b6b6 + 1ddd0d4 commit 015d595

File tree

11 files changed

+351
-94
lines changed

11 files changed

+351
-94
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
musicscan CHANGELOG
22
======================
33

4+
## RELEASE 0.1.7
5+
- [musicscan-57](https://github.com/cjcodeproj/musicscan/issues/57) Release 0.1.7
6+
- [musicscan-36](https://github.com/cjcodeproj/musicscan/issues/36) Update some editing notes
7+
- [musicscan-52](https://github.com/cjcodeproj/musicscan/issues/52) Add new flags to RST documentation
8+
- [musicscan-40](https://github.com/cjcodeproj/musicscan/issues/40) Documentation: flags option flag
9+
- [musicscan-53](https://github.com/cjcodeproj/musicscan/issues/53) Album title is not sanitized
10+
- [musicscan-51](https://github.com/cjcodeproj/musicscan/issues/51) Edit flag for Intro and Reprise
11+
- [musicscan-22](https://github.com/cjcodeproj/musicscan/issues/22) Flag codes should probably be distinct between album and song
12+
- [musicscan-49](https://github.com/cjcodeproj/musicscan/issues/49) id3scan should be a bin tool
13+
14+
415
## RELEASE 0.1.5
516
- [musicscan-46](https://github.com/cjcodeproj/musicscan/issues/46) Release 0.1.5
617
- [musicscan-34](https://github.com/cjcodeproj/musicscan/issues/34) Flag for bonus tracks

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ music collection and builds a set of XML files adhering to the vtmedia schema.
66
It includes a tool that will recursively scan a directory for audio files
77
containing ID3 tags and uses that data as the basis for bulding XML files.
88

9+
10+
* [How It Works](#how-it-works)
11+
* [XML Schema](#xml-schema)
12+
* [Scanning Example](#scanning-example)
13+
* [The Audio CD File](#the-audio-cd-file)
14+
* [The Index File](#the-index-file)
15+
* [The Album File](#the-album-file)
16+
* [Documentation](#documentation)
17+
* [Building And Installing From Source Code](#building-and-installing-from-source-code)
18+
* [Package Distribution](#package-distribution)
19+
20+
921
## How It Works
1022

1123
It works under the assumption that the digital library was created by importing CDs
@@ -52,10 +64,11 @@ $ ls -1 "~/Music/iTunes/iTunes Media/Music/Garth Brooks/No Fences/"
5264
10 Wolves.m4a
5365
```
5466

55-
The `id3scan` tool will search that directory and create three files.
67+
The `id3scan` tool will search that directory and create three files. Make sure the install path for the id3scan tool
68+
matches your environment command path.
5669

5770
```
58-
$ python3 -m musicscan.tools.id3scan --musicpath "~/Music/iTunes/iTunes Media/Music/Garth Brooks/No Fences" --write --outdir ~/tmp --split-xml
71+
$ id3scan --musicpath "~/Music/iTunes/iTunes Media/Music/Garth Brooks/No Fences" --write --outdir ~/tmp --split-xml
5972
```
6073

6174
```

doc/id3scan.rst

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ musisscan.tools.id3scan
55
NAME
66
----
77

8-
musicscan.tools.id3scan - scan files for ID3 metadata to build XML structures
8+
id3scan - scan files for ID3 metadata to build XML structures
99

1010
SYNOPSIS
1111
--------
1212

1313
::
1414

15-
python -m musicscan.tools.id3scan [-h] [--musicpath PATH]
15+
id3scan [-h] [--musicpath PATH]
1616
[--outdir PATH]
1717
[--write/--no-write]
1818
[--split-xml/--no-split-xml]
@@ -48,7 +48,7 @@ OPTIONS
4848
If existing XML files are already in the output path, they will be overwritten.
4949

5050
``--flags``
51-
If specified, additional XML comments will be embedded in the file containing
51+
By default, additional XML comments will be embedded in the file containing context
5252
hints about the data, such as whether a song has a featured guest artist,
5353
or if a track is just filler material from the source CD.
5454

@@ -77,8 +77,8 @@ correctly identify multiple artists, or distinguish between an individual artist
7777
If you don't like the results of your editing, it's possible to just delete the data
7878
and re-generate it by running the id3scan again.
7979

80-
FLAGS
81-
-----
80+
ALBUM FLAGS
81+
-----------
8282

8383
Flags are embedded information in the XML comments that provide information on
8484
how the software has interpreted the metadata
@@ -92,9 +92,19 @@ how the software has interpreted the metadata
9292
``possible_soundtrack``
9393
The album is possibly a soundtrack from a film, television show, or play.
9494

95-
``possible_score```
95+
``possible_score``
9696
The album is possible a musical score from a film, television show, or play.
9797

98+
``possible_tribute``
99+
The album is possibly a tribute to another musical artist.
100+
101+
``possible_intro_and_reprise``
102+
The first track of the album may be an introduction, and the final track may
103+
be a repreise.
104+
105+
TRACK FLAGS
106+
-----------
107+
98108
``detected_square_brackets``
99109
Square brackets were detected in the title of the track, suggesting extra
100110
metadata could be embedded in the title.
@@ -122,12 +132,27 @@ how the software has interpreted the metadata
122132
``possible_blank_track``
123133
The track is possibly a blank track that contains no actual music.
124134

135+
``possible_bonus_track``
136+
It's possible the track is a bonus track, which could appear
137+
on a re-issue of a CD or record.
138+
125139
``country_and_folk_genre_is_too_vague``
126140
The metadata genre value for the track is "Country & Folk", which
127141
actually is two different genres. The value should be changed to
128142
the more accurate value.
129143

144+
``missing_copyright_year``
145+
The copyright year information could be missing.
146+
147+
``missing_artist``
148+
The artist name is missing
149+
150+
``possible_intro_track``
151+
The track could serve as an introduction to the album, or
152+
to another track.
130153

154+
``possible_reprise_track``
155+
The track could serve as a reprise to the album.
131156

132157
ENVIRONMENT VARIABLES
133158
---------------------

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "musicscan"
3-
version = "0.1.5"
3+
version = "0.1.7"
44
description = "Package for converting music metadata to XML"
55
readme = "README.md"
66
requires-python = ">3.8"
@@ -33,6 +33,8 @@ homepage = "https://github.com/cjcodeproj/musicscan"
3333
repository = "https://github.com/cjcodeproj/musicscan"
3434
changelog = "https://github.com/cjcodeproj/musicscan/blob/main/CHANGELOG.md"
3535

36+
[project.scripts]
37+
id3scan = "musicscan.tools.id3scan:main_cli"
3638

3739
[tools.setuptools]
3840
license-files = ["LICENSE"]

src/musicscan/data/analyzer.py

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
Testing code to identify possible edit flags.
2727
'''
2828

29+
# pylint: disable=too-many-public-methods
30+
2931
import re
3032
from musicscan.data.cd import Album
31-
from musicscan.data.flags import FlagCodes
33+
from musicscan.data.flags import AlbumFlagCodes, TrackFlagCodes
3234
from musicscan.data.track import Track
3335

3436

@@ -42,11 +44,25 @@ def main_test(self, in_album: Album):
4244
'''
4345
Main album testing routine.
4446
'''
47+
self.album_tests(in_album)
48+
self.track_tests(in_album)
49+
self.album_closing_tests(in_album)
50+
51+
def album_tests(self, in_album: Album):
52+
'''
53+
Perform tests against the album.
54+
'''
4555
self.check_album_values(in_album)
4656
self.test_album_title(in_album)
4757
self.test_album_artist(in_album)
4858
self.test_soundtrack(in_album)
4959
self.test_score(in_album)
60+
self.test_tribute(in_album)
61+
62+
def track_tests(self, in_album: Album):
63+
'''
64+
Perform tests against every track.
65+
'''
5066
for dsc in sorted(in_album.discs):
5167
for trk in sorted(in_album.discs[dsc].tracks,
5268
key=lambda x: x.track_no):
@@ -57,6 +73,15 @@ def main_test(self, in_album: Album):
5773
self.test_track_genre(trk)
5874
self.test_album_track_blank(trk)
5975
self.test_album_track_bonus(trk)
76+
self.test_album_track_intro(trk)
77+
self.test_album_track_reprise(trk)
78+
79+
def album_closing_tests(self, in_album: Album):
80+
'''
81+
Perform additional tests after all
82+
tracks are analzed.
83+
'''
84+
self.test_intro_reprise_presence(in_album)
6085

6186
def check_album_values(self, in_album: Album):
6287
'''
@@ -65,7 +90,7 @@ def check_album_values(self, in_album: Album):
6590
'''
6691
if not in_album.title:
6792
in_album.title = 'PLACEHOLDER ALBUM TITLE'
68-
in_album.flags.add_flag(FlagCodes.m_album_title)
93+
in_album.flags.add_flag(TrackFlagCodes.m_album_title)
6994

7095
def check_track_values(self, in_track: Track):
7196
'''
@@ -74,7 +99,7 @@ def check_track_values(self, in_track: Track):
7499
'''
75100
if not in_track.artist:
76101
in_track.artist = 'PLACEHOLDER TRACK ARTIST'
77-
in_track.flags.add_flag(FlagCodes.m_track_artist)
102+
in_track.flags.add_flag(TrackFlagCodes.m_track_artist)
78103

79104
def test_album_title(self, in_album: Album):
80105
'''
@@ -84,9 +109,9 @@ def test_album_title(self, in_album: Album):
84109
greatest_p = re.compile(r'Greatest', re.IGNORECASE)
85110
hits_p = re.compile(r'\s+Hit', re.IGNORECASE)
86111
if greatest_p.search(in_album.title):
87-
in_album.flags.add_flag(FlagCodes.p_greatest)
112+
in_album.flags.add_flag(AlbumFlagCodes.p_greatest)
88113
if hits_p.search(in_album.title):
89-
in_album.flags.add_flag(FlagCodes.p_hits_compo)
114+
in_album.flags.add_flag(AlbumFlagCodes.p_hits_compo)
90115

91116
def test_soundtrack(self, in_album: Album):
92117
'''
@@ -96,10 +121,10 @@ def test_soundtrack(self, in_album: Album):
96121
first_track = in_album.first_track()
97122
soundtrack_p = re.compile(r'\s+Soundtrack\s+', re.IGNORECASE)
98123
if soundtrack_p.search(in_album.title):
99-
in_album.flags.add_flag(FlagCodes.p_soundtrack)
124+
in_album.flags.add_flag(AlbumFlagCodes.p_soundtrack)
100125
if first_track.genre == 'Soundtrack':
101-
if FlagCodes.p_soundtrack not in in_album.flags.flags:
102-
in_album.flags.add_flag(FlagCodes.p_soundtrack)
126+
if AlbumFlagCodes.p_soundtrack not in in_album.flags.flags:
127+
in_album.flags.add_flag(AlbumFlagCodes.p_soundtrack)
103128

104129
def test_score(self, in_album: Album):
105130
'''
@@ -108,7 +133,16 @@ def test_score(self, in_album: Album):
108133
'''
109134
score_p = re.compile(r'\s+Score\s+', re.IGNORECASE)
110135
if score_p.search(in_album.title):
111-
in_album.flags.add_flag(FlagCodes.p_score)
136+
in_album.flags.add_flag(AlbumFlagCodes.p_score)
137+
138+
def test_tribute(self, in_album: Album):
139+
'''
140+
Test if the album title suggests that it is a
141+
tribute to another artist.
142+
'''
143+
tribute_p = re.compile(r'\s+Tribute\s+', re.IGNORECASE)
144+
if tribute_p.search(in_album.title):
145+
in_album.flags.add_flag(AlbumFlagCodes.p_tribute)
112146

113147
def test_album_artist(self, in_album: Album):
114148
'''
@@ -117,17 +151,17 @@ def test_album_artist(self, in_album: Album):
117151
'''
118152
and_p = re.compile(r'\s+and\s+', re.IGNORECASE)
119153
if and_p.search(str(in_album.artist)):
120-
in_album.flags.add_flag(FlagCodes.l_group)
154+
in_album.flags.add_flag(TrackFlagCodes.l_group)
121155

122156
def test_album_track_chrs(self, in_track: Track):
123157
'''
124158
Test the track title to indicate metadata may
125159
have been embeded in the track name in parenthesis.
126160
'''
127161
if '[' in in_track.title or ']' in in_track.title:
128-
in_track.flags.add_flag(FlagCodes.d_sq_brackets)
162+
in_track.flags.add_flag(TrackFlagCodes.d_sq_brackets)
129163
if '(' in in_track.title or ')' in in_track.title:
130-
in_track.flags.add_flag(FlagCodes.d_parenthesis)
164+
in_track.flags.add_flag(TrackFlagCodes.d_parenthesis)
131165

132166
def test_album_track_featured(self, in_track: Track):
133167
'''
@@ -137,9 +171,9 @@ def test_album_track_featured(self, in_track: Track):
137171
feature_p = re.compile(r'Feat', re.IGNORECASE)
138172
with_p = re.compile(r'With\s+', re.IGNORECASE)
139173
if feature_p.search(in_track.title) or with_p.search(in_track.title):
140-
in_track.flags.add_flag(FlagCodes.p_feat_artist)
174+
in_track.flags.add_flag(TrackFlagCodes.p_feat_artist)
141175
if feature_p.search(in_track.artist) or with_p.search(in_track.artist):
142-
in_track.flags.add_flag(FlagCodes.p_feat_artist)
176+
in_track.flags.add_flag(TrackFlagCodes.p_feat_artist)
143177

144178
def test_album_track_live(self, in_track: Track):
145179
'''
@@ -148,7 +182,7 @@ def test_album_track_live(self, in_track: Track):
148182
'''
149183
live_p = re.compile(r'Live\s+', re.IGNORECASE)
150184
if live_p.search(in_track.title):
151-
in_track.flags.add_flag(FlagCodes.p_live)
185+
in_track.flags.add_flag(TrackFlagCodes.p_live)
152186

153187
def test_track_genre(self, in_track: Track):
154188
'''
@@ -157,7 +191,7 @@ def test_track_genre(self, in_track: Track):
157191
country_p = re.compile(r'Country & Folk', re.IGNORECASE)
158192
if in_track.genre:
159193
if country_p.search(in_track.genre):
160-
in_track.flags.add_flag(FlagCodes.p_genre_country_folk)
194+
in_track.flags.add_flag(TrackFlagCodes.p_genre_country_folk)
161195

162196
def test_album_track_demo(self, in_track: Track):
163197
'''
@@ -166,7 +200,7 @@ def test_album_track_demo(self, in_track: Track):
166200
'''
167201
demo_p = re.compile(r'Demo', re.IGNORECASE)
168202
if demo_p.search(in_track.title):
169-
in_track.flags.add_flag(FlagCodes.p_demo)
203+
in_track.flags.add_flag(TrackFlagCodes.p_demo)
170204

171205
def test_album_track_blank(self, in_track: Track):
172206
'''
@@ -175,7 +209,7 @@ def test_album_track_blank(self, in_track: Track):
175209
'''
176210
blank_p = re.compile(r'Blank', re.IGNORECASE)
177211
if blank_p.search(in_track.title):
178-
in_track.flags.add_flag(FlagCodes.p_blank_track)
212+
in_track.flags.add_flag(TrackFlagCodes.p_blank_track)
179213

180214
def test_album_track_bonus(self, in_track: Track):
181215
'''
@@ -185,4 +219,42 @@ def test_album_track_bonus(self, in_track: Track):
185219
'''
186220
bonus_p = re.compile(r'\W+Bonus', re.IGNORECASE)
187221
if bonus_p.search(in_track.title):
188-
in_track.flags.add_flag(FlagCodes.p_bonus_track)
222+
in_track.flags.add_flag(TrackFlagCodes.p_bonus_track)
223+
224+
def test_album_track_intro(self, in_track: Track):
225+
'''
226+
Test the track to see if it is an introduction to
227+
another track, or possibly for the album itself.
228+
'''
229+
intro_p = re.compile(r'[\s\W]?Intro', re.IGNORECASE)
230+
if intro_p.search(in_track.title):
231+
in_track.flags.add_flag(TrackFlagCodes.p_intro)
232+
233+
def test_album_track_reprise(self, in_track: Track):
234+
'''
235+
Test the track to see if it is labeled as a
236+
reprise, either to the album, or to a previous
237+
track on the album.
238+
'''
239+
reprise_p = re.compile(r'[\s\W]?Reprise', re.IGNORECASE)
240+
if reprise_p.search(in_track.title):
241+
in_track.flags.add_flag(TrackFlagCodes.p_reprise)
242+
243+
def test_intro_reprise_presence(self, in_album: Album):
244+
'''
245+
Test to see if both an intro and reprise is
246+
contained in the album, and add an additional
247+
album level flag to identify it.
248+
'''
249+
reprise = False
250+
intro = False
251+
for dsc in sorted(in_album.discs):
252+
for trk in sorted(in_album.discs[dsc].tracks,
253+
key=lambda x: x.track_no):
254+
if TrackFlagCodes.p_intro in trk.flags:
255+
intro = True
256+
if TrackFlagCodes.p_reprise in trk.flags:
257+
reprise = True
258+
if intro and reprise:
259+
in_album.flags.add_flag(AlbumFlagCodes.p_intro_reprise)
260+
break

0 commit comments

Comments
 (0)