C#实现音乐在线播放和下载——Windows程序设计作业3

news2024/11/29 10:54:19

1. 作业内容

    编写一个C#程序,在作业二实现的本地播放功能的基础上,新增在线播放和在线下载功能,作业二博客地址:C#实现简单音乐文件解析播放——Windows程序设计作业2

2. 架构选择

    考虑到需求中的界面友好和跨版本兼容性,我选择选择WinForms作为开发平台,WinForms提供了一个简单而强大的方法来创建桌面应用程序,并且与C#高度兼容,在开发过程,选择.NETFramework 4.8.0版本进行程序设计。

3. UI设计

    在本次程序设计中,我设计了一套UI界面,并且利用panelbutton控件实现简单的界面切换,一共分为三个界面:本地播放界面、在线下载界面和在线播放界面,具体界面如下:

此处使用的图标来源:FLATICON

本地播放节目
在线下载界面
在线播放界面
    以下为本次作业涉及到的控件:
    1) Button:用于输入歌名实现搜索功能。
    2) TextBox:用于触发各种操作,例如播放、搜索、下载等。
    3) Panel:用于切换界面。
    4) ListBox:下载用于显示搜索结果列表或歌单列表,用户可以从中选择歌曲。
    5) ProgressBar:用于显示进度。
    6) AxWindowsMediaPlayer:用于播放音乐。

4. 主界面设计

    主界面中主要是围绕各个界面的切换设置的,其中将本地播放音乐界面作为主界面UCHome,剩余两个功能界面定义为UserControl对象。

// 定义各界面对象
public UCHome uchome;
public UserControl1 userControl1;
public UserControl2 userControl2;

    切换界面的思路是,利用panel控件作为主要的切换面板,在切换界面的时候,清空其中的界面并添加新的界面,从而实现界面切换功能。

uchome = new UCHome();    //实例化
uchome.Show(); // 将窗体一进行显示
panelContain.Controls.Clear(); // 清空原容器上的控件
panelContain.Controls.Add(uchome); // 将窗体一加入容器panelContain

5. 音乐API说明

    在本次作业中,设计在线下载和播放使用的音乐api接口是调用了别人进行二次加工后的api接口,简化了开发流程,特别感谢,但不方便透露,故不在此展出,项目链接在最后放出,如有需要,在下载后调整api接口和返回的参数信息处理,仍可实现功能。

6. 功能说明与代码实现

6.1 本地播放音乐

    此项功能主要是辨析音乐文件的格式并且针对不同格式的文件采取不同的解析方式,在作业二中已经实现,此处不再过多阐述。

6.2 在线下载音乐

    在线下载音乐功能可以分为三个模块实现:搜索歌曲、选择想要下载的歌曲、下载音乐,最终效果如下:

6.2.1 搜索歌曲

    搜索歌曲模块通过用户输入的歌曲名称,向远程API发送请求并获取搜索结果。
    1) 初始化参数

private int selectedIndex = -1; // 当前选中的索引
private List<string> responseLines = new List<string>(); // 保存响应结果的列表

    2) 用户在文本框TextBox中输入歌曲名称。

string songName = textBox1.Text;
if (string.IsNullOrWhiteSpace(songName))
{
    MessageBox.Show("请输入歌曲名称");
    return;
}

    3) 点击搜索按钮后,调用SearchSongsAsync方法发送HTTP请求,获取包含搜索结果的响应,将结果处理好显示在ListBox中供用户选择。

private async void button1_Click(object sender, EventArgs e)
{	
	/*
		此处省略了前面的输入部分
	*/
    listBox1.Items.Clear();
    responseLines.Clear();
    var responseBody = await SearchSongsAsync(songName); // 调用搜索歌曲的异步方法
    if (!string.IsNullOrEmpty(responseBody))
    {
        var lines = responseBody.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
        foreach (var line in lines)
        {
            responseLines.Add(line);
            listBox1.Items.Add(line); // 将结果添加到列表框中
        }
    }
    else
    {
        MessageBox.Show("未找到歌曲");
    }
}

private async Task<string> SearchSongsAsync(string songName)
{
    string apiUrl = $"https://www.zbx123456?msg={Uri.EscapeDataString(songName)}";

    using (HttpClient client = new HttpClient())
    {
        HttpResponseMessage response = await client.GetAsync(apiUrl);
        response.EnsureSuccessStatusCode();

        string responseBody = await response.Content.ReadAsStringAsync();
        return responseBody;
    }
}

    此处采取async修饰函数和Task<string>作为返回值是因为在WindoForms应用程序中,如果进行长时间的同步操作(如网络请求、文件读写),会导致界面卡顿或无响应。通过使用asyncTask,可以将这些耗时操作异步执行,从而保持用户界面的响应能力。
    其中,async 用于声明异步方法,允许函数中使用await函数,await允许程序在执行异步操作时,暂停方法执行,释放UI线程以处理其他任务。当异步操作完成后,恢复方法执行。
    Task<string>表示一个异步操作,该操作最终会返回一个string结果,使异步方法在逻辑上与同步方法一致,方便处理返回值。

6.2.2 选择想要下载的歌曲

    用户在搜索结果列表中选择想要下载的歌曲,通过SelectedIndexChanged事件处理程序更新选中的索引。

// 列表框选中项改变事件处理程序
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    selectedIndex = listBox1.SelectedIndex + 1;
}

6.2.3 下载音乐

    下载音乐模块根据选中的歌曲索引获取下载链接,并下载音乐文件到用户指定的路径。
    1) 用户点击下载按钮,调用button2_Click事件处理程序,先检查是否有选中的歌曲,并异步获取下载链接。

private async void button2_Click(object sender, EventArgs e)
{
    if (selectedIndex == -1 || selectedIndex >= responseLines.Count)
    {
        MessageBox.Show("请选择要下载的歌曲");
        return;
    }

    string downloadUrl = await GetDownloadUrlAsync(selectedIndex); // 调用获取下载链接的异步方法
	/*
    	下载音乐逻辑,后面详细介绍
    */
}

    2) 调用GetDownloadUrlAsync方法获取选中歌曲的下载链接:

private async Task<string> GetDownloadUrlAsync(int n)
{
    string apiUrl = $"https://www.zbx123456?msg={Uri.EscapeDataString(textBox1.Text)}&n={n}";

    using (HttpClient client = new HttpClient())
    {
        HttpResponseMessage response = await client.GetAsync(apiUrl);
        response.EnsureSuccessStatusCode();

        string responseBody = await response.Content.ReadAsStringAsync();
        textBox2.Text = responseBody; // 将响应内容放入textBox2中

        // 检查响应内容,解析播放链接
        var lines = responseBody.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);

        foreach (var line in lines)
        {
            if (line.StartsWith("播放链接:"))
            {
                return line.Substring(5).Trim();
            }
        }
    }

    return null;
}

    这里需要对api接口重新处理使因为在搜索歌曲后,根据选择的歌曲不同,api接口中的参数需要进行调整才能返回这首歌曲的详细参数,其中的播放链接指向这首歌曲的下载地址,利用这个地址,便可以在线下载音乐。
    3) 弹出提示,用户自行选择下载路径,选择后,调用DownloadSongAsync方法去下载音乐。

if (!string.IsNullOrEmpty(downloadUrl))
    {
        string fileName = $"{Guid.NewGuid()}.flac"; // 用GUID作为文件名

        using (SaveFileDialog saveFileDialog = new SaveFileDialog())
        {
            saveFileDialog.FileName = fileName;
            saveFileDialog.Filter = "FLAC files (*.flac)|*.flac|All files (*.*)|*.*";
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                progressBar1.Value = 0; // 重置进度条
                await DownloadSongAsync(downloadUrl, saveFileDialog.FileName); // 调用下载歌曲的异步方法
                MessageBox.Show("下载完成");
            }
        }
    }
    else
    {
        MessageBox.Show("未找到播放链接");
    }

    4) 下载文件,并提示下载进度

private async Task DownloadSongAsync(string url, string filePath)
{
	//设置最长响应时间:30min
    using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromMinutes(30) })
    {
        // 获取响应流
        using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
        {
        	//确保响应成功
            response.EnsureSuccessStatusCode();
            long? totalBytes = response.Content.Headers.ContentLength;

            using (Stream contentStream = await response.Content.ReadAsStreamAsync(),
                   fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
            {
                var buffer = new byte[8192];
                long totalReadBytes = 0;
                int readBytes;

                while ((readBytes = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await fileStream.WriteAsync(buffer, 0, readBytes);
                    totalReadBytes += readBytes;

                    if (totalBytes.HasValue)
                    {
                        // 更新进度条
                        progressBar1.Value = (int)((totalReadBytes * 100) / totalBytes.Value);
                    }
                }
            }
        }
    }
}

    这里同样采取awaitTask设计,除此之外,此处利用response.Content.Headers.ContentLength获取响应内容的总字节大小,并采取HTTP客户端->缓冲区->文件的流程下载文件,在下载中记录目前已经下载的字节数totalReadBytes,实现下载进度条。
    至此,整个在线下载音乐功能实现完毕,用户可以自己搜索关键词,下载音乐。

疑似咪咕音乐的资源不多,能选的有点少~

6.3 在线播放音乐

    在线下载音乐功能可以分为三个模块实现:搜索歌曲、选择想要播放的歌曲、播放音乐,实现效果如下:

6.3.1 搜索歌曲 && 6.3.2 选择想要播放的歌曲

    此处的实现思路和在线下载音乐的思路一样的,不在此展出了。

6.3.3 播放音乐

    用户点击播放按钮,调用button3_Click事件处理程序,先检查是否有选中的歌曲,并异步调用GetPlayUrlAsync方法获取播放链接,将链接传入PlayAudio函数中调用AxWindowsPlayer控件实现在线播放功能:
    button3_click的代码实现如下:

private async void button3_Click(object sender, EventArgs e)
{
    if (selectedIndex == -1 || selectedIndex >= responseLines.Count)
    {
        MessageBox.Show("请选择要播放的歌曲");
        return;
    }

    string playUrl = await GetPlayUrlAsync(selectedIndex); // 调用获取播放链接的异步方法
    
    if (!string.IsNullOrEmpty(playUrl))
    {
        PlayAudio(playUrl); // 调用播放音乐的方法
    }
    else
    {
        MessageBox.Show("未找到播放链接");
    }
}

    GetPlayUrlAsync方法与前面的 GetDownloadUrlAsync方法几乎一致,只改变了函数名,就不展示了~
    PlayAudio函数中调用AxWindowsPlayer控件实现在线播放功能,在作业二中只使用了该控件的本地播放功能,其实它可以通过传递url参数实现在线播放功能。

不得不说,查文档大的时候才意识到,可能上课讲过,一下子记不得了,折腾了好久,原来的思路是使用WebBrower控件去实现,这样的缺点是,播放器是调用的本地自己的播放器,不太方便,故选用这种方式。

public void PlayAudio(string url)
{
    axWindowsMediaPlayer1.URL = url;
    axWindowsMediaPlayer1.Ctlcontrols.play();
}

    针对在线播放功能,我也仿照前面本地播放功能实现了音量控制、暂停播放和下一首:

private async void button2_Click(object sender, EventArgs e)
{
    if (responseLines.Count > 0)
    {
        int index = (selectedIndex + 1);

        if (index >= responseLines.Count())
        {
            index = 0;
        }

        string playUrl = await GetPlayUrlAsync(index); // 调用获取播放链接的异步方法

        PlayAudio(playUrl); 

        selectedIndex = index;
        listBox1.SelectedIndex = index - 1;
    }
}

private void trackBar1_Scroll(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.settings.volume = trackBar1.Value;
}

private void button4_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.Ctlcontrols.stop(); // 停止播放
}

这里其实应该要像本地播放那样针对不同格式的音乐进行不同的处理,但是我这个API返回的好像是只有MP4格式,就让我偷个懒吧~

7. 设计难点分析

    本次作业中我认为最难的应该是那个异步处理部分和文件下载部分,网络连接可以通过文档一步步调整,但是这个异步处理的思路,我最开始是没有想到的,后来还是问了ChatGPT才解决的,文件下载我最开始以为是直接可以一步到位下载到文件(因为我在网页中打开链接的时候是直接下载的),忽略了缓冲区,老是爆各种各样的错误。

8. 完整代码地址

    C#实现音乐在线播放和下载——Windows程序设计作业3

9. 总结&改进思路

    这次的项目设计本地的音乐播放和在线音乐的搜索、选择、下载和播放功能,总体上是一个较为完整的项目了。
    尽管目前的功能已经能够满足基本的需求,但仍有许多改进和优化的空间。以下是一些可能的改进思路:
    1) 增强用户体验:可以在搜索结果的基础上,增加分页、排序、过滤功能。
    2) 提高代码可维护性:可以引入MVVM模式将业务和UI逻辑分离,提升代码的可读性。
    3) 拓展功能:可以拓展用户功能,用户可以个性化搜索和收藏歌单,与其他人分享自己存在本地的歌单。
    期待未来能够再次更新此项目,实现更多的功能~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1851068.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】基础IO_3

文章目录 六、基础I/O3. 软硬链接4. 动静态库 未完待续 六、基础I/O 3. 软硬链接 使用 ln 就可以创建链接&#xff0c;使用 ln -s 可以创建软链接&#xff0c;直接使用 ln 则是硬链接。 我们对硬链接进行测试一下&#xff1a; 根据测试&#xff0c;我们知道了 硬链接就像一…

Django框架数据库ORM查询操作

Django框架在生成数据库的models模型文件后&#xff0c;旧可以在应用中通过ORM来操作数据库了。今天抽空试了下查询语句。以下是常用的查询语句。 以下查询需要引入django的Sum&#xff0c;Count&#xff0c;Q模块 from django.db.models import Sum,Count,Q 导入生成的mode…

【FreeRTOS】删除任务 用遥控器控制音乐

参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》 学习视频&#xff1a;【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS&#xff08;FreeRTOS教程 基于STM32&#xff0c;以实际项目为导向&#xff09;】 【精准空降到 01:22】 https://www.bilibili.com/video/BV1…

App Store苹果应用商店如何退款

这几天想在App Store找一款录音可以转文字的app&#xff0c;结果找到后需要订阅才能转文字&#xff1b;最短1个月38元&#xff0c;购买之后&#xff0c;试用了功能&#xff0c;发现转出来的文字差强人意&#xff0c;且好多语音漏过了没转出来&#xff1b;于是决定取消订阅&…

ArkUI部分案例笔记——padding,space

基础的构建 组件分类&#xff1a; 容器组件&#xff1a;像Column&#xff0c;Row这种组件就是容器组件一般就来控制行和列的就是容器组件 基础组件&#xff1a;Text(文本组件)&#xff0c;像这种用来有一定功能的就是基础组件 注意&#xff1a;一个build只能有一个根容器组件…

8.12 矢量图层面要素单一符号使用五(栅格数据填充)

文章目录 前言栅格数据填充&#xff08;Raster image fill&#xff09;QGis设置面符号为栅格数据填充&#xff08;Raster image fill&#xff09;二次开发代码实现栅格数据填充&#xff08;Raster image fill&#xff09; 总结 前言 本章介绍矢量图层线要素单一符号中使用栅格…

XXL-Job实战(千万级短信推送实战)

上回我们介绍了传统定时任务与分布式任务调度的差异以及它们的优缺点&#xff0c;本节我们使用Xxl-job来实现相关需求。 首先我们需要下载Xxl-job对应的服务端&#xff1b;下面是Xxl-job的github地址&#xff1a; Releases xuxueli/xxl-job (github.com) 版本我们选择V-2.3…

【iOS】编译二进制文件说明

编译二进制文件说明 如何生成文件路径文件说明第一部分&#xff1a;.o文件第二部分&#xff1a;link第三部分&#xff1a;Segment第四部分&#xff1a;Symbol 如何生成 使用Xcode进行编译 &#xff0c;会生成二进制相关文件&#xff0c;可以更详细看产物的布局 项目Target -&…

Python统计实战:一题搞定多元线性回归、共线性、相对重要性分析

为了解决特定问题而进行的学习是提高效率的最佳途径。这种方法能够使我们专注于最相关的知识和技能&#xff0c;从而更快地掌握解决问题所需的能力。 &#xff08;以下练习题来源于《统计学—基于Python》。联系获取完整数据和Python源代码文件。&#xff09; 练习题 为了分析…

自动控制原理出射角计算

背景&#xff1a;突然发现自己出射角不会算 被减数是零点到极点的角度&#xff0c;减数是极点到极点的角度

十大经典排序算法——插入排序与希尔排序(超详解)

一、插入排序 1.基本思想 直接插入排序是一种简单的插入排序法&#xff0c;基本思想是&#xff1a;把待排序的记录按其数值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列。 2.直接插入排序 当插入第 e…

NSIS 入门教程 (二)

引言 在教程的第一部分中创建第一个安装程序后,我们还将需要删除其安装区段中已安装的文件。我们还将展示更多安装引导页面&#xff0c;让用户有机会选择安装的某些部分。 卸载 创建一个安装程序.可以干净的卸载,不仅是一种礼貌&#xff0c;对于程序的开发与发行方也有很…

【鸿蒙】 模拟器运⾏

【鸿蒙】HUAWEI DevEco Studio安装-CSDN博客 【鸿蒙】创建第⼀个鸿蒙项⽬-CSDN博客 点击 Tools 菜单下的 Device Manager 点击 Install &#xff0c;安装模拟器 下载模拟器相关的SDK&#xff0c;点击 Finish 选择安装⽬录&#xff0c;点击 New Emulator 选择设备类型&#…

大疆炸机后MOV修复方法(DJI Inspire 3)

dji大疆可以说是无人机中的华为&#xff0c;产品线之广性能之高让高傲的美国人侧面&#xff0c;质量和性价比才是王道。另外产品线的细分也是制胜法宝&#xff0c;无论是手持、农用机、特殊无人机还是影视级产品DJI都有涉及&#xff0c;给人的感觉就是在无人机细分方面它已经无…

LeetCode 算法:排序链表 c++

原题链接&#x1f517;&#xff1a;排序链表 难度&#xff1a;中等⭐️⭐️ 题目 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [4,2,1,3] 输出&#xff1a;[1,2,3,4] 示例 2&#xff1a; 输…

Windows11系统自动获取电脑IPV6地址,并且开机自动发送到指定邮箱

废话&#xff1a;最近放假回家&#xff0c;在家里突然想玩游戏了&#xff0c;Steamdeck性能终归有限。部分游戏始终玩的不爽&#xff0c;想到之前了解到的SunshnieMoonlight串流的方案&#xff0c;远程调用家里的电脑打游戏&#xff0c;简直不要太爽。 一顿折腾之后配置好了所有…

C语言| 数组的顺序查找

顺序查找 查找数组a中第一次出现数字m的下标&#xff0c;并输出该下标&#xff1b; 如果没有则输出sorry。 1 定义变量 数组a&#xff0c;n表示数组的个数&#xff0c; m要查找的数字 2 用sizeof()函数&#xff0c;求出数组元素的个数 3 从键盘中任意输出一个数字m&#xff0c;…

Docker网络介绍

网络是虚拟化技术中最复杂的部分&#xff0c;也是Docker应用中的一个重要环节。 Docker中的网络主要解决容器与容器、容器与外部网络、外部网络与容器之间的互相通信的问题。 这些复杂情况的存在要求Docker有一个强大的网络功能去保障其网络的稳健性。因此&#xff0c;Docker…

象战----第十二届中山市邀请赛正赛

本次的题解一定让大家享受脑细胞碰撞与再生死亡的感受&#xff01;定然酣畅淋漓&#xff01;请耐心的读完 简称&#xff1a;让脑袋死机。。。 象战 老规矩先分析在打码&#xff1a; 注意到题目告诉我们&#xff1a;四个角落是不能放的 那么 我们设象在(i,j).(注意&#xff1a…

120.网络游戏逆向分析与漏洞攻防-邮件系统数据分析-邮件发送功能的封装

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…