diff --git a/plugins/remove_[data_track]s/remove_[data_track]s.py b/plugins/remove_[data_track]s/remove_[data_track]s.py
new file mode 100644
index 00000000..60f9c75a
--- /dev/null
+++ b/plugins/remove_[data_track]s/remove_[data_track]s.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2024-2025 cerenkov (crnkv)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+PLUGIN_NAME = 'Remove [data track]s'
+PLUGIN_AUTHOR = 'cerenkov (crnkv)'
+PLUGIN_DESCRIPTION = '''
+Skip and remove all [data track]s and [silence] tracks once the album is loaded. Also shift and fix the track numbers.
+
+This plugin intends to present the track list in line with online digital music platforms or CD ripper (re-)distributors. For example:
+
+- Track #00: [pregap] audio track -> No change. Maintain track number #00
+- Track #01: [data track] -> Remove
+- Track #02: audio track -> Change track number to #01
+- Track #03: [data track] -> Remove
+- Track #04: audio track -> Change track number to #02
+- Track #05: [crowd noise] yet still an audio track -> Change track number to #03
+- Track #06: [silence] empty audio track -> Remove
+- Track #07: hidden audio track -> Change track number to #04
+- -- totaltracks: 8 --
+
+The result will be like:
+
+- Track #00: [pregap] audio track
+- Track #01: audio track
+- Track #02: audio track
+- Track #03: [crowd noise] yet still an audio track
+- Track #04: hidden audio track
+- -- totaltracks: 5 --
+
+This is automatic once the album is loaded. Users wouldn't be aware of the existence of [data track]s and [silence] tracks.
+
+Examples: releases of MBID 9cd9e81a-2dab-46d0-988e-bb486ddc1b05 and 9c0b5a23-ca6e-4b4e-be2f-98280cf56c88
+
+Its difference with a similar-purpose script of Persistent Variables is that this plugin can skip any number of [data track]s on any positions, and that the hidden variables %_absolutetracknumber% and %_totalalbumtracks% will also be fixed.
+
+More information on special purpose track titles: https://musicbrainz.org/doc/Style/Unknown_and_untitled/Special_purpose_track_title
+'''
+PLUGIN_VERSION = '0.1'
+PLUGIN_API_VERSIONS = ['2.0', '2.7', '2.8', '2.9']
+PLUGIN_LICENSE = 'GPL-2.0-or-later'
+PLUGIN_LICENSE_URL = 'https://www.gnu.org/licenses/gpl-2.0.html'
+
+from picard import log
+from picard.metadata import register_album_metadata_processor
+from picard.plugin import PluginPriority
+
+
+# This is a data track if:
+# Track Title / Recording Title is [data track], or Track Artist / Recording Artist is [data]
+# Bonus: also skip when Track Title / Recording Title is [silence]
+# See: https://github.com/metabrainz/picard/blob/master/picard/track.py#L315 for the method '_customize_metadata(self)'
+def isDataTrack(track):
+ return track.get('title', '') == '[data track]' \
+ or track.get('recording', {}).get('title', '') == '[data track]' \
+ or track.get('artist-credit', [{}])[0].get('artist', {}).get('name', '') == '[data]' \
+ or track.get('recording', {}).get('artist-credit', [{}])[0].get('artist', {}).get('name', '') == '[data]' \
+ or track.get('title', '') == '[silence]' \
+ or track.get('recording', {}).get('title', '') == '[silence]'
+
+def remove_datatracks_from_release(album, metadata, release):
+ try:
+ for disc in release['media']:
+ datatrack_positions = []
+
+ # Examine the pregap track, assuming its position is 0
+ if isDataTrack(disc.get('pregap', {})):
+ del disc['pregap'];
+
+ # Remove the 'data-tracks' node (never seen in real album releases, not sure about its structure)
+ # Its impact on the track positions in the 'tracks' node is unverified
+ # See: https://github.com/metabrainz/picard/blob/master/picard/album.py#L447 for the method '_load_tracks(self)'
+ if 'data-tracks' in disc:
+ for track in disc['data-tracks']:
+ if 'position' in track:
+ datatrack_positions.append(track['position'])
+ del disc['data-tracks']
+
+ # Examine each track under the 'tracks' node
+ for i in reversed(range(len(disc['tracks']))):
+ track = disc['tracks'][i]
+ if isDataTrack(track):
+ # The track['number'] field usually conforms with the %_musicbrainz_tracknumber% metadata, which can be vinyl numbering (A1, A2…). This is not to be used.
+ # Assuming that the track['position'] field coincides with the usual %tracknumber% metadata, which are integers starting from 1, even when a pregap track exists in the medium.
+ datatrack_positions.append(track['position'])
+ del disc['tracks'][i]
+
+ disc['track-count'] = len(disc['tracks']) # pregap doesn't count (as seen in real album releases)
+ for track in disc['tracks']:
+ position = track['position']
+ number_to_skip = len([p for p in datatrack_positions if p < position])
+ track['position'] = position - number_to_skip
+ if len(datatrack_positions) > 0:
+ log.info("[Info] Removed {0} data / silence tracks, original positions: {1}".format(len(datatrack_positions), sorted(datatrack_positions)))
+ # Assuming that after the remove_datatracks_from_release function call, MusicBrainz Picard will infer / calculate the metadata %tracknumber% %totaltracks% %_absolutetracknumber% %_totalalbumtracks% from track['position'] fields of the modified release Object, that nothing else is needed to care about.
+ except Exception as ex:
+ log.error("[Error] {0}".format(ex))
+
+register_album_metadata_processor(remove_datatracks_from_release, priority=PluginPriority.HIGH)
+