[banshee] Metadata ratings and playcount import and export (bgo#532650)
- From: Alexander Kojevnikov <alexk src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [banshee] Metadata ratings and playcount import and export (bgo#532650)
- Date: Tue, 17 Nov 2009 04:03:55 +0000 (UTC)
commit 16047c8167559da34a8b9b3ce7b9106c7a1dc59b
Author: Nicholas Parker <nickbp gmail com>
Date: Tue Nov 17 15:00:54 2009 +1100
Metadata ratings and playcount import and export (bgo#532650)
Signed-off-by: Alexander Kojevnikov <alexander kojevnikov com>
.../Banshee.Base/Tests/TaglibReadWriteTests.cs | 8 +-
.../Banshee.Configuration.Schema/LibrarySchema.cs | 7 +
src/Core/Banshee.Core/Banshee.Core.csproj | 4 +-
.../Banshee.Streaming/StreamRatingTagger.cs | 380 ++++++++++++++++++++
.../Banshee.Core/Banshee.Streaming/StreamTagger.cs | 72 +++--
src/Core/Banshee.Core/Makefile.am | 1 +
.../DatabaseImportManager.cs | 3 +-
.../DatabaseTrackInfo.cs | 40 ++-
.../Banshee.Metadata/SaveTrackMetadataJob.cs | 7 +-
.../Banshee.Metadata/SaveTrackMetadataService.cs | 20 +-
.../Banshee.Preferences/PreferenceService.cs | 3 +-
.../Banshee.Library.Gui/ImportDialog.cs | 1 +
.../Banshee.Dap.MassStorage/MassStorageSource.cs | 4 +-
13 files changed, 498 insertions(+), 52 deletions(-)
---
diff --git a/src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs b/src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs
index ffe6bba..8a94b8b 100644
--- a/src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs
+++ b/src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs
@@ -180,13 +180,13 @@ namespace Banshee.Base.Tests
change (track);
// Save changes
- bool saved = StreamTagger.SaveToFile (track);
+ bool saved = StreamTagger.SaveToFile (track, true, true);
Assert.IsTrue (saved);
// Read changes
file = StreamTagger.ProcessUri (uri);
track = new TrackInfo ();
- StreamTagger.TrackInfoMerge (track, file);
+ StreamTagger.TrackInfoMerge (track, file, false, true);
// Verify changes
verify (track);
@@ -200,6 +200,8 @@ namespace Banshee.Base.Tests
track.TrackNumber = 4;
track.DiscNumber = 4;
track.Year = 1999;
+ track.Rating = 2;
+ track.PlayCount = 3;
}
private void VerifyTrack (TrackInfo track)
@@ -210,6 +212,8 @@ namespace Banshee.Base.Tests
Assert.AreEqual (4, track.TrackNumber);
Assert.AreEqual (4, track.DiscNumber);
Assert.AreEqual (1999, track.Year);
+ Assert.AreEqual (2, track.Rating);
+ Assert.AreEqual (3, track.PlayCount);
}
private Type unix_io_type;
diff --git a/src/Core/Banshee.Core/Banshee.Configuration.Schema/LibrarySchema.cs b/src/Core/Banshee.Core/Banshee.Configuration.Schema/LibrarySchema.cs
index a83fe4f..6a17f7d 100644
--- a/src/Core/Banshee.Core/Banshee.Configuration.Schema/LibrarySchema.cs
+++ b/src/Core/Banshee.Core/Banshee.Configuration.Schema/LibrarySchema.cs
@@ -77,5 +77,12 @@ namespace Banshee.Configuration.Schema
"Write metadata back to audio files",
"If enabled, metadata (tags) will be written back to audio files when using the track metadata editor."
);
+
+ public static readonly SchemaEntry<bool> WriteRatingsAndPlayCounts = new SchemaEntry<bool>(
+ "library", "write_rating",
+ false,
+ "Store ratings within supported files",
+ "If enabled, rating and playcount metadata will be written back to audio files."
+ );
}
}
diff --git a/src/Core/Banshee.Core/Banshee.Core.csproj b/src/Core/Banshee.Core/Banshee.Core.csproj
index aa201bd..f1bda3c 100644
--- a/src/Core/Banshee.Core/Banshee.Core.csproj
+++ b/src/Core/Banshee.Core/Banshee.Core.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -111,6 +111,8 @@
<Compile Include="Banshee.Collection\TrackFilterType.cs" />
<Compile Include="Banshee.Base\PlatformHacks.cs" />
<Compile Include="Banshee.Streaming\CommonTags.cs" />
+ <Compile Include="Banshee.Streaming\SaveTrackMetadataJob.cs" />
+ <Compile Include="Banshee.Streaming\StreamRatingTagger.cs" />
<Compile Include="Banshee.Streaming\StreamTag.cs" />
<Compile Include="Banshee.Streaming\StreamTagger.cs" />
<Compile Include="Banshee.Streaming\StreamPlaybackError.cs" />
diff --git a/src/Core/Banshee.Core/Banshee.Streaming/StreamRatingTagger.cs b/src/Core/Banshee.Core/Banshee.Streaming/StreamRatingTagger.cs
new file mode 100644
index 0000000..0551c28
--- /dev/null
+++ b/src/Core/Banshee.Core/Banshee.Streaming/StreamRatingTagger.cs
@@ -0,0 +1,380 @@
+//
+// StreamRatingTagger.cs
+//
+// Author:
+// Nicholas Parker <nickbp gmail com>
+//
+// Copyright (C) 2008-2009 Nicholas Parker
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using Banshee.Collection;
+using System.Collections;
+
+namespace Banshee.Streaming
+{
+ internal static class ID3v2RatingTagger
+ {
+ // What we call ourselves in POPM tags.
+ private static string POPM_our_creator_name = "Banshee";
+
+ // Ordered list of ID3v2 POPM authors to attempt when importing.
+ // Banshee must be listed first, to ensure that we give priority to our own ratings.
+ // If new entries are added to this list, also make sure that
+ // PopmToBanshee and BansheeToPopm are still accurate.
+ private static string[] POPM_known_creator_list = {
+ POPM_our_creator_name,// This item must be first
+ "quodlibet lists sacredchao net",// Quod Libet (their default)
+ "Windows Media Player 9 Series",// WMP/Vista
+ "no email",// MediaMonkey
+ "mcored gmail com" // iTSfv
+ };
+
+ // Converts ID3v2 POPM rating to Banshee rating
+ private static int PopmToBanshee (byte popm_rating)
+ {
+ // The following schemes are used by the other POPM-compatible players:
+ // WMP/Vista: "Windows Media Player 9 Series" ratings:
+ // 1 = 1, 2 = 64, 3=128, 4=196 (not 192), 5=255
+ // MediaMonkey: "no email" ratings:
+ // 0.5=26, 1=51, 1.5=76, 2=102, 2.5=128,
+ // 3=153, 3.5=178, 4=204, 4.5=230, 5=255
+ // Quod Libet: "quodlibet lists sacredchao net" ratings
+ // (but that email can be changed):
+ // arbitrary scale from 0-255
+ // Compatible with all these rating scales (what we'll use):
+ // unrated=0, 1=1-63, 2=64-127, 3=128-191, 4=192-254, 5=255
+ if (popm_rating == 0x0)// unrated
+ return 0;
+ if (popm_rating < 0x40)// 1-63
+ return 1;
+ if (popm_rating < 0x80)// 64-127
+ return 2;
+ if (popm_rating < 0xC0)// 128-191
+ return 3;
+ if (popm_rating < 0xFF)// 192-254
+ return 4;
+ return 5;// 255
+ }
+
+ // Converts Banshee rating to ID3v2 POPM rating
+ private static byte BansheeToPopm (int banshee_rating)
+ {
+ switch (banshee_rating) {
+ case 1:
+ return 0x1;
+ case 2:
+ return 0x40;// 64
+ case 3:
+ return 0x80;// 128
+ case 4:
+ return 0xC0;// 192
+ case 5:
+ return 0xFF;// 255
+ default:
+ return 0x0;// unrated/unknown
+ }
+ }
+
+ private static TagLib.Id3v2.Tag GetTag (TagLib.File file)
+ {
+ try {
+ return file.GetTag (TagLib.TagTypes.Id3v2) as TagLib.Id3v2.Tag;
+ } catch (System.NullReferenceException e) {
+ // TagLib# can crash here on unusual files (Ex: FLAC files with ID3v2 metadata)
+ // Perhaps FLAC+ID3v2 is an unsupported combination for TagLib#?
+ Hyena.Log.WarningFormat ("Got exception when accessing ID3v2 Metadata in {0}:",
+ file.Name);
+ Hyena.Log.Warning (e.Message);
+ Hyena.Log.Warning (e.StackTrace);
+ return null;
+ }
+ }
+
+ // Overwrites all POPM frames with the new rating and playcount.
+ // If no *known-compatible* frames are found, a new "Banshee"-authored
+ // frame is also created to store this information.
+ public static void StoreRatingAndPlayCount (int rating, int playcount,
+ TagLib.File to_file)
+ {
+ TagLib.Id3v2.Tag id3v2tag = GetTag (to_file);
+ if (id3v2tag == null) {
+ return;
+ }
+
+ bool known_frames_found = false;
+ foreach (TagLib.Id3v2.PopularimeterFrame popm in
+ id3v2tag.GetFrames<TagLib.Id3v2.PopularimeterFrame> ()) {
+ if (System.Array.IndexOf (POPM_known_creator_list, popm.User) >= 0) {
+ // Found a known-good POPM frame, don't need to create a "Banshee" frame.
+ known_frames_found = true;
+ }
+
+ popm.Rating = BansheeToPopm (rating);
+ popm.PlayCount = (ulong)playcount;
+ Hyena.Log.DebugFormat ("Exporting ID3v2 Rating={0}({1}) and Playcount={2}({3}) to File \"{4}\" as Creator \"{5}\"",
+ rating, popm.Rating,
+ playcount, popm.PlayCount,
+ to_file.Name, popm.User);
+ }
+
+ if (!known_frames_found) {
+ // No known-good frames found, create a new POPM frame (with creator string "Banshee")
+ TagLib.Id3v2.PopularimeterFrame popm = TagLib.Id3v2.PopularimeterFrame.Get (id3v2tag,
+ POPM_our_creator_name,
+ true);
+ popm.Rating = BansheeToPopm (rating);
+ popm.PlayCount = (ulong)playcount;
+ Hyena.Log.DebugFormat ("Exporting ID3v2 Rating={0}({1}) and Playcount={2}({3}) to File \"{4}\" as Creator \"{5}\"",
+ rating, popm.Rating,
+ playcount, popm.PlayCount,
+ to_file.Name, POPM_our_creator_name);
+ }
+ }
+
+ // Scans the file for *known-compatible* POPM frames, with priority given to
+ // frames at the top of the known creator list.
+ public static void GetRatingAndPlayCount (TagLib.File from_file,
+ ref int rating, ref int playcount)
+ {
+ TagLib.Id3v2.Tag id3v2tag = GetTag (from_file);
+ if (id3v2tag == null) {
+ return;
+ }
+
+ TagLib.Id3v2.PopularimeterFrame popm = null;
+ for (int i = 0; i < POPM_known_creator_list.Length; i++) {
+ popm = TagLib.Id3v2.PopularimeterFrame.Get (id3v2tag,
+ POPM_known_creator_list[i],
+ false);
+ if (popm != null) {
+ break;
+ }
+ }
+
+ if (popm != null) {
+ rating = PopmToBanshee (popm.Rating);
+ playcount = (int)popm.PlayCount;
+ Hyena.Log.DebugFormat ("Importing ID3v2 Rating={0}({1}) and Playcount={2}({3}) from File \"{4}\" as Creator \"{5}\"",
+ rating, popm.Rating,
+ playcount, popm.PlayCount,
+ from_file.Name, popm.User);
+ }
+ }
+ }
+
+ // Applicable for Vorbis, Speex, and many (most?) FLAC files
+ // Follows the naming standard established by the Quod Libet team
+ // See: http://code.google.com/p/quodlibet/wiki/Specs_VorbisComments
+ internal static class OggRatingTagger
+ {
+ // What we call ourselves in rating/playcount tags.
+ private static string ogg_our_creator_name = "BANSHEE";
+
+ // Prefix to rating field names (lowercase)
+ private static string rating_prefix = "RATING:";
+
+ // Prefix to playcount field names (lowercase)
+ private static string playcount_prefix = "PLAYCOUNT:";
+
+ // Converts Ogg rating to Banshee rating
+ private static int OggToBanshee (string ogg_rating_str)
+ {
+ double ogg_rating = double.Parse (ogg_rating_str);
+ // Quod Libet Ogg ratings are stored as a value
+ // between 0.0 and 1.0 inclusive, where unrated = 0.5.
+ if (ogg_rating == 0.5)// unrated
+ return 0;
+ if (ogg_rating > 0.8)// (0.8,1.0]
+ return 5;
+ if (ogg_rating > 0.6)// (0.6,0.8]
+ return 4;
+ if (ogg_rating > 0.4)// (0.4,0.5),(0.5,0.6]
+ return 3;
+ if (ogg_rating > 0.2)// (0.2,0.4]
+ return 2;
+ else // [0.0,0.2]
+ return 1;
+ }
+
+ // Converts Banshee rating to Ogg rating
+ private static string BansheeToOgg (int banshee_rating)
+ {
+ // I went with this scaling so that if we switch to fractional stars
+ // in the future (such as "0.25 stars"), we'll have room for that.
+ switch (banshee_rating) {
+ case 1:
+ return "0.2";
+ case 2:
+ return "0.4";
+ case 3:
+ return "0.6";
+ case 4:
+ return "0.8";
+ case 5:
+ return "1.0";
+ default:
+ return "0.5";// unrated/unknown
+ }
+ }
+
+ private static TagLib.Ogg.XiphComment GetTag (TagLib.File file)
+ {
+ try {
+ return file.GetTag (TagLib.TagTypes.Xiph) as TagLib.Ogg.XiphComment;
+ } catch (System.NullReferenceException e) {
+ // Haven't seen crashes when getting Ogg tags, but just in case..
+ // (See commentary for ID3v2 version)
+ Hyena.Log.WarningFormat ("Got exception when accessing Ogg Metadata in {0}:",
+ file.Name);
+ Hyena.Log.Warning (e.Message);
+ Hyena.Log.Warning (e.StackTrace);
+ return null;
+ }
+ }
+
+ // Scans the file for ogg rating/playcount tags as defined by the Quod Libet standard
+ // If a Banshee tag is found, it is given priority.
+ // If a Banshee tag is not found, the last rating/playcount tags found are used
+ public static void GetRatingAndPlayCount (TagLib.File from_file,
+ ref int rating, ref int playcount)
+ {
+ TagLib.Ogg.XiphComment xiphtag = GetTag (from_file);
+ if (xiphtag == null) {
+ return;
+ }
+
+ bool banshee_rating_done = false, banshee_playcount_done = false;
+ string rating_raw = "", playcount_raw = "";
+
+ foreach (string fieldname in xiphtag) {
+
+ if (!banshee_rating_done &&
+ fieldname.ToUpper ().StartsWith (rating_prefix)) {
+
+ rating_raw = xiphtag.GetFirstField (fieldname);
+ string rating_creator = fieldname.Substring (rating_prefix.Length);
+ if (rating_creator.ToUpper () == ogg_our_creator_name) {
+ // We made this rating, consider it authoritative.
+ banshee_rating_done = true;
+ // Don't return -- we might not have seen a playcount yet.
+ }
+
+ } else if (!banshee_playcount_done &&
+ fieldname.ToUpper ().StartsWith (playcount_prefix)) {
+
+ playcount_raw = xiphtag.GetFirstField (fieldname);
+ string playcount_creator = fieldname.Substring (playcount_prefix.Length);
+ if (playcount_creator.ToUpper () == ogg_our_creator_name) {
+ // We made this playcount, consider it authoritative.
+ banshee_playcount_done = true;
+ // Don't return -- we might not have seen a rating yet.
+ }
+ }
+ }
+ if (rating_raw != "") {
+ rating = OggToBanshee (rating_raw);
+ }
+ if (playcount_raw != "") {
+ playcount = int.Parse (playcount_raw);
+ }
+ Hyena.Log.DebugFormat ("Importing Ogg Rating={0}({1}) and Playcount={2}({3}) from File \"{4}\"",
+ rating, rating_raw,
+ playcount, playcount_raw, from_file.Name);
+ }
+
+ // Scans the file for ogg rating/playcount tags as defined by the Quod Libet standard
+ // All applicable tags are overwritten with the new values, regardless of tag author
+ public static void StoreRatingAndPlayCount (int rating, int playcount,
+ TagLib.File to_file)
+ {
+ TagLib.Ogg.XiphComment xiphtag = GetTag (to_file);
+ if (xiphtag == null) {
+ return;
+ }
+
+ ArrayList rating_fieldnames = new ArrayList ();
+ ArrayList playcount_fieldnames = new ArrayList ();
+
+ // Collect list of rating and playcount tags to be updated:
+ foreach (string fieldname in xiphtag) {
+ if (fieldname.ToUpper ().StartsWith (rating_prefix)) {
+ rating_fieldnames.Add (fieldname);
+ } else if (fieldname.ToUpper ().StartsWith (playcount_prefix)) {
+ playcount_fieldnames.Add (fieldname);
+ }
+ }
+ // Add "BANSHEE" tags if no rating/playcount tags were found:
+ if (rating_fieldnames.Count == 0) {
+ rating_fieldnames.Add (rating_prefix+ogg_our_creator_name);
+ }
+ if (playcount_fieldnames.Count == 0) {
+ playcount_fieldnames.Add (playcount_prefix+ogg_our_creator_name);
+ }
+
+ string ogg_rating = BansheeToOgg (rating);
+ foreach (string ratingname in rating_fieldnames) {
+ xiphtag.SetField (ratingname, ogg_rating);
+ Hyena.Log.DebugFormat ("Exporting Ogg Rating={0}({1}) to File \"{2}\" as Creator \"{3}\"",
+ rating, ogg_rating,
+ to_file.Name,
+ ratingname.Substring (rating_prefix.Length));
+ }
+ string ogg_playcount = playcount.ToString ();
+ foreach (string playcountname in playcount_fieldnames) {
+ xiphtag.SetField (playcountname, ogg_playcount);
+ Hyena.Log.DebugFormat ("Exporting Ogg Playcount={0}({1}) to File \"{2}\" as Creator \"{3}\"",
+ playcount, ogg_playcount,
+ to_file.Name,
+ playcountname.Substring (playcount_prefix.Length));
+ }
+ }
+ }
+
+ public static class StreamRatingTagger
+ {
+ public static void GetRatingAndPlayCount (TagLib.File from_file,
+ ref int rating, ref int playcount)
+ {
+ if ((from_file.Tag.TagTypes & TagLib.TagTypes.Id3v2) != 0) {
+ ID3v2RatingTagger.GetRatingAndPlayCount (from_file,
+ ref rating, ref playcount);
+ }
+ if ((from_file.Tag.TagTypes & TagLib.TagTypes.Xiph) != 0) {
+ OggRatingTagger.GetRatingAndPlayCount (from_file,
+ ref rating, ref playcount);
+ }
+ }
+
+ public static void StoreRatingAndPlayCount (int rating, int playcount,
+ TagLib.File to_file)
+ {
+ if ((to_file.Tag.TagTypes & TagLib.TagTypes.Id3v2) != 0) {
+ ID3v2RatingTagger.StoreRatingAndPlayCount (rating, playcount,
+ to_file);
+ }
+ if ((to_file.Tag.TagTypes & TagLib.TagTypes.Xiph) != 0) {
+ OggRatingTagger.StoreRatingAndPlayCount (rating, playcount,
+ to_file);
+ }
+ }
+ }
+}
diff --git a/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs b/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
index 1260560..589dfaa 100644
--- a/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
+++ b/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
@@ -134,6 +134,11 @@ namespace Banshee.Streaming
public static void TrackInfoMerge (TrackInfo track, TagLib.File file, bool preferTrackInfo)
{
+ TrackInfoMerge (track, file, preferTrackInfo, false);
+ }
+
+ public static void TrackInfoMerge (TrackInfo track, TagLib.File file, bool preferTrackInfo, bool import_rating_and_play_count)
+ {
// TODO support these as arrays:
// Performers[] (track artists), AlbumArtists[], Composers[], Genres[]
@@ -170,6 +175,13 @@ namespace Banshee.Streaming
track.DiscCount = Choose ((int)file.Tag.DiscCount, track.DiscCount, preferTrackInfo);
track.Year = Choose ((int)file.Tag.Year, track.Year, preferTrackInfo);
track.Bpm = Choose ((int)file.Tag.BeatsPerMinute, track.Bpm, preferTrackInfo);
+
+ if (import_rating_and_play_count) {
+ int file_rating = 0, file_playcount = 0;
+ StreamRatingTagger.GetRatingAndPlayCount (file, ref file_rating, ref file_playcount);
+ track.Rating = Choose (file_rating, track.Rating, preferTrackInfo);
+ track.PlayCount = Choose (file_playcount, track.PlayCount, preferTrackInfo);
+ }
} else {
track.MediaAttributes = TrackMediaAttributes.AudioStream;
if (track.Uri != null && VideoExtensions.IsMatchingFile (track.Uri.LocalPath)) {
@@ -239,7 +251,7 @@ namespace Banshee.Streaming
} catch {}
}
- public static bool SaveToFile (TrackInfo track)
+ public static bool SaveToFile (TrackInfo track, bool write_metadata, bool write_rating_and_play_count)
{
// FIXME taglib# does not seem to handle writing metadata to video files well at all atm
// so not allowing
@@ -254,31 +266,39 @@ namespace Banshee.Streaming
return false;
}
- file.Tag.Performers = new string [] { track.ArtistName };
- file.Tag.PerformersSort = new string [] { track.ArtistNameSort };
- file.Tag.Album = track.AlbumTitle;
- file.Tag.AlbumSort = track.AlbumTitleSort;
- file.Tag.AlbumArtists = track.AlbumArtist == null ? new string [0] : new string [] {track.AlbumArtist};
- file.Tag.AlbumArtistsSort = (track.AlbumArtistSort == null ? new string [0] : new string [] {track.AlbumArtistSort});
- // Bug in taglib-sharp-2.0.3.0: Crash if you send it a genre of "{ null }"
- // on a song with both ID3v1 and ID3v2 metadata. It's happy with "{}", though.
- // (see http://forum.taglib-sharp.com/viewtopic.php?f=5&t=239 )
- file.Tag.Genres = (track.Genre == null) ? new string[] {} : new string [] { track.Genre };
- file.Tag.Title = track.TrackTitle;
- file.Tag.TitleSort = track.TrackTitleSort;
- file.Tag.Track = (uint)track.TrackNumber;
- file.Tag.TrackCount = (uint)track.TrackCount;
- file.Tag.Composers = new string [] { track.Composer };
- file.Tag.Conductor = track.Conductor;
- file.Tag.Grouping = track.Grouping;
- file.Tag.Copyright = track.Copyright;
- file.Tag.Comment = track.Comment;
- file.Tag.Disc = (uint)track.DiscNumber;
- file.Tag.DiscCount = (uint)track.DiscCount;
- file.Tag.Year = (uint)track.Year;
- file.Tag.BeatsPerMinute = (uint)track.Bpm;
-
- SaveIsCompilation (file, track.IsCompilation);
+ if (write_metadata) {
+ file.Tag.Performers = new string [] { track.ArtistName };
+ file.Tag.PerformersSort = new string [] { track.ArtistNameSort };
+ file.Tag.Album = track.AlbumTitle;
+ file.Tag.AlbumSort = track.AlbumTitleSort;
+ file.Tag.AlbumArtists = track.AlbumArtist == null ? new string [0] : new string [] {track.AlbumArtist};
+ file.Tag.AlbumArtistsSort = (track.AlbumArtistSort == null ? new string [0] : new string [] {track.AlbumArtistSort});
+ // Bug in taglib-sharp-2.0.3.0: Crash if you send it a genre of "{ null }"
+ // on a song with both ID3v1 and ID3v2 metadata. It's happy with "{}", though.
+ // (see http://forum.taglib-sharp.com/viewtopic.php?f=5&t=239 )
+ file.Tag.Genres = (track.Genre == null) ? new string[] {} : new string [] { track.Genre };
+ file.Tag.Title = track.TrackTitle;
+ file.Tag.TitleSort = track.TrackTitleSort;
+ file.Tag.Track = (uint)track.TrackNumber;
+ file.Tag.TrackCount = (uint)track.TrackCount;
+ file.Tag.Composers = new string [] { track.Composer };
+ file.Tag.Conductor = track.Conductor;
+ file.Tag.Grouping = track.Grouping;
+ file.Tag.Copyright = track.Copyright;
+ file.Tag.Comment = track.Comment;
+ file.Tag.Disc = (uint)track.DiscNumber;
+ file.Tag.DiscCount = (uint)track.DiscCount;
+ file.Tag.Year = (uint)track.Year;
+ file.Tag.BeatsPerMinute = (uint)track.Bpm;
+
+ SaveIsCompilation (file, track.IsCompilation);
+ }
+
+ if (write_rating_and_play_count) {
+ // FIXME move StreamRatingTagger to taglib#
+ StreamRatingTagger.StoreRatingAndPlayCount (track.Rating, track.PlayCount, file);
+ }
+
file.Save ();
track.FileSize = Banshee.IO.File.GetSize (track.Uri);
diff --git a/src/Core/Banshee.Core/Makefile.am b/src/Core/Banshee.Core/Makefile.am
index de474b2..ccf03e9 100644
--- a/src/Core/Banshee.Core/Makefile.am
+++ b/src/Core/Banshee.Core/Makefile.am
@@ -63,6 +63,7 @@ SOURCES = \
Banshee.Kernel/Scheduler.cs \
Banshee.Streaming/CommonTags.cs \
Banshee.Streaming/StreamPlaybackError.cs \
+ Banshee.Streaming/StreamRatingTagger.cs \
Banshee.Streaming/StreamTag.cs \
Banshee.Streaming/StreamTagger.cs
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs
index 5b6d5c7..03afd5d 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs
@@ -40,6 +40,7 @@ using Banshee.Base;
using Banshee.Sources;
using Banshee.Collection;
using Banshee.Collection.Database;
+using Banshee.Configuration.Schema;
using Banshee.ServiceStack;
using Banshee.Streaming;
@@ -175,7 +176,7 @@ namespace Banshee.Collection.Database
try {
track = new DatabaseTrackInfo ();
track.Uri = uri;
- StreamTagger.TrackInfoMerge (track, StreamTagger.ProcessUri (uri));
+ StreamTagger.TrackInfoMerge (track, StreamTagger.ProcessUri (uri), false, true);
track.PrimarySource = trackPrimarySourceChooser (track);
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
index cfdedff..a96c644 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
@@ -39,6 +39,8 @@ using Hyena.Query;
using Banshee.Base;
using Banshee.Configuration.Schema;
using Banshee.Database;
+using Banshee.Metadata;
+using Banshee.Preferences;
using Banshee.Query;
using Banshee.Sources;
using Banshee.ServiceStack;
@@ -72,6 +74,33 @@ namespace Banshee.Collection.Database
Provider.Copy (original, this);
}
+ // Changing these fields shouldn't change DateUpdated (which triggers file save)
+ private static readonly HashSet<QueryField> transient_fields;
+
+ static DatabaseTrackInfo ()
+ {
+ transient_fields = new HashSet<QueryField> () {
+ BansheeQuery.ScoreField,
+ BansheeQuery.SkipCountField,
+ BansheeQuery.LastSkippedField,
+ BansheeQuery.LastPlayedField,
+ BansheeQuery.PlaybackErrorField,
+ BansheeQuery.PlayCountField,
+ BansheeQuery.RatingField
+ };
+ Action<Root> handler = delegate {
+ if (SaveTrackMetadataService.WriteRatingsAndPlayCountsEnabled.Value) {
+ transient_fields.Remove (BansheeQuery.PlayCountField);
+ transient_fields.Remove (BansheeQuery.RatingField);
+ } else {
+ transient_fields.Add (BansheeQuery.PlayCountField);
+ transient_fields.Add (BansheeQuery.RatingField);
+ }
+ };
+ SaveTrackMetadataService.WriteRatingsAndPlayCountsEnabled.ValueChanged += handler;
+ handler (null);
+ }
+
public override void OnPlaybackFinished (double percentCompleted)
{
if (ProviderRefresh()) {
@@ -127,17 +156,6 @@ namespace Banshee.Collection.Database
Save (NotifySaved);
}
- // Changing these fields shouldn't change DateUpdated (which triggers file save)
- private static HashSet<QueryField> transient_fields = new HashSet<QueryField> {
- BansheeQuery.ScoreField,
- BansheeQuery.SkipCountField,
- BansheeQuery.LastSkippedField,
- BansheeQuery.PlayCountField,
- BansheeQuery.LastPlayedField,
- BansheeQuery.RatingField,
- BansheeQuery.PlaybackErrorField
- };
-
public void Save (bool notify, params QueryField [] fields_changed)
{
// If either the artist or album changed,
diff --git a/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs b/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs
index 52081e3..c999d69 100644
--- a/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs
+++ b/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs
@@ -63,7 +63,8 @@ namespace Banshee.Metadata
);
}
- public bool WriteEnabled { get; set; }
+ public bool WriteMetadataEnabled { get; set; }
+ public bool WriteRatingsAndPlayCountsEnabled { get; set; }
public bool RenameEnabled { get; set; }
private HyenaSqliteCommand update_synced_at;
@@ -75,9 +76,9 @@ namespace Banshee.Metadata
bool wrote = false;
bool renamed = false;
try {
- if (WriteEnabled) {
+ if (WriteMetadataEnabled || WriteRatingsAndPlayCountsEnabled) {
Hyena.Log.DebugFormat ("Saving metadata for {0}", track);
- wrote = StreamTagger.SaveToFile (track);
+ wrote = StreamTagger.SaveToFile (track, WriteMetadataEnabled, WriteRatingsAndPlayCountsEnabled);
}
if (RenameEnabled) {
diff --git a/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs b/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs
index e2bcc98..2587aa9 100644
--- a/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs
+++ b/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs
@@ -43,12 +43,17 @@ namespace Banshee.Metadata
{
public class SaveTrackMetadataService : IService
{
- public static SchemaPreference<bool> WriteEnabled = new SchemaPreference<bool> (
+ public static SchemaPreference<bool> WriteMetadataEnabled = new SchemaPreference<bool> (
LibrarySchema.WriteMetadata,
Catalog.GetString ("Write _metadata to files"),
Catalog.GetString ("Save tags and other metadata inside supported media files")
);
+ public static SchemaPreference<bool> WriteRatingsAndPlayCountsEnabled = new SchemaPreference<bool> (
+ LibrarySchema.WriteRatingsAndPlayCounts,
+ Catalog.GetString ("Write _ratings and play counts to files"),
+ Catalog.GetString ("Enable this option to save rating and playcount metadata inside supported audio files."));
+
public static SchemaPreference<bool> RenameEnabled = new SchemaPreference<bool> (
LibrarySchema.MoveOnInfoSave,
Catalog.GetString ("_Update file and folder names"),
@@ -62,7 +67,8 @@ namespace Banshee.Metadata
public SaveTrackMetadataService ()
{
Banshee.ServiceStack.Application.RunTimeout (10000, delegate {
- WriteEnabled.ValueChanged += OnEnabledChanged;
+ WriteMetadataEnabled.ValueChanged += OnEnabledChanged;
+ WriteRatingsAndPlayCountsEnabled.ValueChanged += OnEnabledChanged;
RenameEnabled.ValueChanged += OnEnabledChanged;
ServiceManager.SourceManager.MusicLibrary.TracksChanged += OnTracksChanged;
Save ();
@@ -84,16 +90,18 @@ namespace Banshee.Metadata
private void Save ()
{
- if (!(WriteEnabled.Value || RenameEnabled.Value))
+ if (!(WriteMetadataEnabled.Value || WriteRatingsAndPlayCountsEnabled.Value || RenameEnabled.Value))
return;
lock (sync) {
if (job != null) {
- job.WriteEnabled = WriteEnabled.Value;
+ job.WriteMetadataEnabled = WriteMetadataEnabled.Value;
+ job.WriteRatingsAndPlayCountsEnabled = WriteRatingsAndPlayCountsEnabled.Value;
job.RenameEnabled = RenameEnabled.Value;
} else {
var new_job = new SaveTrackMetadataJob ();
- new_job.WriteEnabled = WriteEnabled.Value;
+ new_job.WriteMetadataEnabled = WriteMetadataEnabled.Value;
+ new_job.WriteRatingsAndPlayCountsEnabled = WriteRatingsAndPlayCountsEnabled.Value;
new_job.RenameEnabled = RenameEnabled.Value;
new_job.Finished += delegate { lock (sync) { job = null; } };
job = new_job;
@@ -109,7 +117,7 @@ namespace Banshee.Metadata
private void OnEnabledChanged (Root pref)
{
- if (WriteEnabled.Value || RenameEnabled.Value) {
+ if (WriteMetadataEnabled.Value || WriteRatingsAndPlayCountsEnabled.Value || RenameEnabled.Value) {
Save ();
} else {
if (job != null) {
diff --git a/src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs b/src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs
index bcea2da..3b4d63d 100644
--- a/src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs
+++ b/src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs
@@ -56,7 +56,8 @@ namespace Banshee.Preferences
policies.Add (new SchemaPreference<bool> (LibrarySchema.CopyOnImport,
Catalog.GetString ("Co_py files to media folders when importing")));
- policies.Add (Banshee.Metadata.SaveTrackMetadataService.WriteEnabled);
+ policies.Add (Banshee.Metadata.SaveTrackMetadataService.WriteMetadataEnabled);
+ policies.Add (Banshee.Metadata.SaveTrackMetadataService.WriteRatingsAndPlayCountsEnabled);
policies.Add (Banshee.Metadata.SaveTrackMetadataService.RenameEnabled);
// Misc section
diff --git a/src/Core/Banshee.ThickClient/Banshee.Library.Gui/ImportDialog.cs b/src/Core/Banshee.ThickClient/Banshee.Library.Gui/ImportDialog.cs
index a3696e4..722abcd 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Library.Gui/ImportDialog.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Library.Gui/ImportDialog.cs
@@ -33,6 +33,7 @@ using Glade;
using Mono.Unix;
+using Banshee.Configuration.Schema;
using Banshee.Sources;
using Banshee.ServiceStack;
using Banshee.Gui;
diff --git a/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs b/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
index 8150950..ba753a0 100644
--- a/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
+++ b/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
@@ -492,7 +492,9 @@ namespace Banshee.Dap.MassStorage
if (track.LastSyncedStamp >= Hyena.DateTimeUtil.ToDateTime (track.FileModifiedStamp)) {
Log.DebugFormat ("Copying Metadata to File Since Sync time >= Updated Time");
- Banshee.Streaming.StreamTagger.SaveToFile (copied_track);
+ bool write_metadata = Metadata.SaveTrackMetadataService.WriteMetadataEnabled.Value;
+ bool write_ratings_and_playcounts = Metadata.SaveTrackMetadataService.WriteRatingsAndPlayCountsEnabled.Value;
+ Banshee.Streaming.StreamTagger.SaveToFile (copied_track, write_metadata, write_ratings_and_playcounts);
}
copied_track.Save (false);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]