首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从MusicBrainz服务获取元数据

从MusicBrainz服务获取元数据
EN

Code Review用户
提问于 2021-02-08 12:18:44
回答 2查看 262关注 0票数 2

我写了一个程序,做了以下几件事:

  • 调用第三方实用程序获取mp3文件指纹
  • 从MusicBrainz服务询问AcoustID ID
  • 从MusicBrainz查询每个ID的元数据

我以前从未在C#中使用过网络,所以可能在我的代码中犯了一些错误(或者它的某些部分可能不可靠)。这就是我发这个的原因。

我的目标是JSON4.0,并希望避免使用第三方外部库与远程主机通信、反序列化.NET文档等。

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

namespace ConsoleApplication16
{
    [DataContract]
    class acoustid_mbids
    {
        [DataContract]
        public class result{
            [DataMember]
            public double score { get; set; }
            [DataMember]
            public string id { get; set; }
            [DataMember]
            public recording[] recordings { get; set; }
        }

        [DataContract]
        public class recording
        {
            [DataMember]
            public string id { get; set; }
        }

        [DataMember]
        public string status { get; set; }
        [DataMember]
        public result[] results { get; set; }
    }

    [DataContract]
    class recording
    {
        [DataMember]
        public string title { get; set; }
        [DataMember]
        public string length { get; set; }
        [DataMember(Name = "artist-credit")]
        public artist[] artists { get; set; }
        [DataMember]
        public genre[] genres { get; set; }
        [DataMember]
        public release[] releases { get; set; }

        [DataContract]
        public class artist
        {
            [DataMember]
            public string name;
            [DataMember]
            public string joinphrase;
        }

        [DataContract]
        public class genre
        {
            [DataMember]
            public int count;
            [DataMember]
            public string name;
        }

        [DataContract]
        public class release
        {
            [DataMember]
            public string title;
            [DataMember]
            public string date;
            [DataMember]
            public string id; //required for fetching artwork
        }
    }

    

    class Program
    {
        public static List<recording> avail_recordings = new List<recording>();
        public static List<string> fpcalc_output = new List<string>();
        public const string API_KEY="xxxxxxx";
        public static HttpWebRequest request;
        public static HttpWebResponse response;
        static void Main(string[] args)
        {
            DataContractJsonSerializer parseJson;
            using (Process proc = new Process())
            {
                proc.StartInfo.FileName = "fpcalc.exe";
                proc.StartInfo.Arguments = " Kalimba.mp3";
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.UseShellExecute = false;
                proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
                proc.Start();
                proc.BeginOutputReadLine();
                proc.WaitForExit();
            }
            ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072;
            request = (HttpWebRequest)HttpWebRequest.Create("http://api.acoustid.org/v2/lookup?client="+API_KEY+"&duration="+fpcalc_output[0].Replace("DURATION=",String.Empty)+"&fingerprint="+fpcalc_output[1].Replace("FINGERPRINT=",String.Empty)+"&meta=recordingids");
            request.Method = "GET";
            response = (HttpWebResponse)request.GetResponse();
            parseJson = new DataContractJsonSerializer(typeof(acoustid_mbids));
            acoustid_mbids acoustid_session = (acoustid_mbids)parseJson.ReadObject(response.GetResponseStream());
            parseJson = new DataContractJsonSerializer(typeof(recording));
            response.Close();
            response = null;
            for (int i = 0; i < acoustid_session.results.Length; i++)
            {
                for (int j = 0; j < acoustid_session.results[i].recordings.Length; j++)
                {
                    request = (HttpWebRequest)HttpWebRequest.Create("http://musicbrainz.org/ws/2/recording/" + acoustid_session.results[i].recordings[j].id + "?fmt=json&inc=artist-credits+releases+genres");
                    request.KeepAlive = false;
                    request.Method = "GET";
                    request.UserAgent = "SimpleID3Editor/1.0 ( maxvoloshin71@mail.ru )";
                    try
                    {
                        response = (HttpWebResponse)request.GetResponse();
                    }
                    catch (WebException ex)
                    {
                        Console.WriteLine((int)((HttpWebResponse)ex.Response).StatusCode);
                        break;
                    }
                    if (response != null)
                    {
                        avail_recordings.Add((recording)parseJson.ReadObject(response.GetResponseStream()));
                        response.Close();
                    }
                }
            }
            
             
            Console.ReadKey();
        }

        static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            Console.WriteLine("Called!");
            fpcalc_output.Add(e.Data);
            
        }
    }
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2021-02-08 14:17:22

以下是我的观察:

  • 有几个未使用的名称空间
    • 右键单击名称空间,然后选择删除和排序使用菜单项。

  • acoustid_mbids \ recording:一般的指导方针是在类名上使用Pascal大小写。
    • 因此,AcoustidMbidsRecording将是建议的名称。
    • 请为子类应用这些命名。

  • DataMember:当您指定它的Name属性时,这个属性也会非常有用。
    • 这样,您就可以将json元素的命名与应用程序逻辑分隔开来:

代码语言:javascript
复制
[DataContract(Name = "acoustid_mbids")]
class AcoustidMbids
{
    [DataContract(Name = "result")]
    public class Result
    {
        [DataMember(Name ="score" )]
        public double Score { get; set; }
        [DataMember(Name = "id")]
        public string Id { get; set; }
        [DataMember(Name = "recordings")]
        public Recording[] Recordings { get; set; }
    }
    
    ...
}
  • Main:请尽量避免将所有内容都放入Main方法中。
    • 试着分成更小的块,如下所示:

代码语言:javascript
复制
static void Main(string[] args)
{
    List<string> fpcalc_output = GetMetaFromMP3("Kalimba.mp3");
    AcoustidMbids acoustid_session = GetMetaFromAcoustid(fpcalc_output);
    List<Recording> avail_recordings = GetMetaFromMusicbrainz(acoustid_session);
}
  • static HttpWebRequest request:请尽量避免重用HttpWebRequestHttpWebResponse对象。
    • 每个方法(GetMetaFromAcoustidGetMetaFromMusicbrainz)都应该处理它们自己的实例。

  • proc.StartInfo.FileName = "fpcalc.exe";:你可以独立进程及其ProcessStartInfo设置
    • 这可能会增加可读性:

代码语言:javascript
复制
var processInfo = new ProcessStartInfo
{
    FileName = "fpcalc.exe",
    Arguments = " Kalimba.mp3",
    RedirectStandardOutput = true,
    UseShellExecute = false,
};

using (Process proc = Process.Start(processInfo))
{
    proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
    proc.BeginOutputReadLine();
    proc.WaitForExit();
}
  • new DataReceivedEventHandler(proc_OutputDataReceived):您可以使用Lambda表达式创建匿名委托:
代码语言:javascript
复制
proc.OutputDataReceived += (s, e) =>
{
    Console.WriteLine("Called!");
    fpcalc_output.Add(e.Data);
};
  • (SecurityProtocolType)3072:请尽量避免使用神奇的数字,而使用常量。
    • Tls12不是.Net Framework4.0的一部分( .NET 4.5中的引种 )

代码语言:javascript
复制
const int Tls12 = 3072;
...
ServicePointManager.SecurityProtocol |= (SecurityProtocolType)Tls12;
  • request = (HttpWebRequest)HttpWebRequest.Create(:如前所述,请为每个调用尝试使用单独的实例。
    • GET是默认方法,因此不必指定:

代码语言:javascript
复制
var request = (HttpWebRequest)WebRequest.Create(url);
  • "&duration=" + fpcalc_output[0].Replace("DURATION=", String.Empty)
    • 请尝试将数据检索逻辑与url创建分开:

代码语言:javascript
复制
var duration = fpcalc_output[0].Replace("DURATION=", string.Empty);
var fingerprint = fpcalc_output[1].Replace("FINGERPRINT=", string.Empty);
var url = "http://api.acoustid.org/v2/lookup?client=" + API_KEY + "&duration=" + duration + "&fingerprint=" + fingerprint + "&meta=recordingids";
  • 请尽量选择string.Empty而不是String.Empty。请阅读此undefined以更好地理解差异。
  • 还请记住,如果集合中没有那么多元素,fpcalc_output[1]可以抛出OutOfRangeException
    • 尝试使用一些解析逻辑来使代码更加健壮:

代码语言:javascript
复制
string durationKey = "DURATION="
int durationIdx = fpcalc_output.FindIndex(a => a.Contains(durationKey));
if(durationIdx == -1) throw new InvalidOperationException("Duration is missing from the fpcalc result"); 
fpcalc_output[durationIdx].Replace(durationKey, string.Empty);
  • 您还应该考虑使用一些内置实用程序来构造QueryParameters,例如:System.Web.HttpUtility.ParseQueryString(相关SO主题)。
  • response = (HttpWebResponse)request.GetResponse():在执行任何进一步的操作之前,请检查响应的StatusCode
    • 如果它与预期的不同,那么web调用很可能失败,因此进一步运行代码是没有意义的。

代码语言:javascript
复制
response = (HttpWebResponse)request.GetResponse();
if(response.StatusCode != HttpStatusCode.OK)
    throw new InvalidOperationException("acoustid did not respond in the expected way.");
  • response.GetResponseStream():请记住,这里使用的是流。如果不需要的话,就应该处理掉。相关SO主题
  • parseJson.ReadObject:如果服务响应与预期结构不同,这会引发SerializationException。为这种情况做好准备,使您的解决方案更加健壮。
  • for (int i = 0; i < acoustid_session.results.Length; i++):优先于foreach循环(这可以极大地提高可读性:
代码语言:javascript
复制
foreach (var result in acoustid_session.results)
foreach (var record in result.Recordings)
{
    ...
}
  • response.Close():如前所述,与显式调用Close相比,请更喜欢使用。
票数 3
EN

Code Review用户

发布于 2021-02-08 14:20:58

一些简短的评论:

  • 正确地命名事物。avail_recordingsfpcalc_output不遵循微软的指导原则(例如,不要使用"snake case",不要不必要地缩写,.)。
  • request = (HttpWebRequest)HttpWebRequest.Create("http://api.acoustid.org/v2/lookup?client="+API_KEY+"&duration="+fpcalc_output[0].Replace("DURATION=",String.Empty)+"&fingerprint="+fpcalc_output[1].Replace("FINGERPRINT=",String.Empty)+"&meta=recordingids");几乎是不可能阅读的。考虑使用类似于new List<KeyValuePair<string, string>>的内容,并将查询字符串的每个键/值对存储为KeyValuePair<string, string>,然后使用.Select(x => string.Format("{0}={1}", x.Key, x.Value))使用NameValueCollection从该集合编译查询字符串。
  • 将代码分成更小的方法/类,这些方法/类只做一件事。有一个方法调用fpcalc,另一个方法调用acoustid.org,另一个方法调用musicbrainz.org,等等。
  • 考虑到除了访问集合中的特定项之外,您从来不使用ij,为什么不使用foreach呢?
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/255756

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档