python读取歌曲时长,并计算歌曲如何组合使得时长等于目标数字

1. python如何读取mp3、flac等音乐类型的歌曲时长

个人觉得python中的tinytag库最好用

一开始还找了另一种实现方式,不过发现不能读取flac类型的,就去找其他实现,然后就找到tinytag库。【方法2(第二个代码模块)最好用!不管什么类型都可以,而且不止可以看持续时长,可以把后面的.duration删掉可以发现名称、专辑、歌手等信息都有】

import librosa
   
"""method 1"""
for list1 in path_list1:
    for index, elements in enumerate(os.listdir(list1)):
        duration = int(librosa.get_duration(path=list1 + '\\' + elements))
        len_list.append(duration)
        name_list.append(elements)
from tinytag import TinyTag

"""method 2"""
for list1 in path_list1:
    for index, elements in enumerate(os.listdir(list1)):
        duration = TinyTag.get(list1 + '\\' + elements).duration
        len_list.append(int(duration))
        name_list.append(elements)

“获取时长都在倒数第3行duration”


2. 指定一个数字,计算出一首或两首歌的时长与该数最接近

【目前用不到3首的,但是感觉以后可以两首跟1首结合就是3首了,但是1、2首写得有点乱,抽象起来可能会有点麻烦】

下面是自己设计的歌曲时长相加或者单首歌曲时长等于目标值,返回歌曲名称和歌曲时长,其实还设计了精度,就是歌曲各种组合,使得与用户给出的目标值相差在一定的秒数误差内。

下面是源码,【缺点是排序算法设计的不好(运行的慢,好在当时歌曲量就500多首),但是当时着急用,想着先写出来,有时间再改】

其中,需要根据情况修改的地方:

  1. 开头第8行的精度jing_du, 歌曲组合后的时长与目标时长 的差值 的绝对值最大不超过该精度
  2. 紧接着的下面的path_list是我电脑中歌曲存放的路径
  3. 最后main函数开头那里指定了目标数字是输入的,可能会视情况而定,也可以不改。

import os
from tinytag import TinyTag

len_list = []  # 用于存储歌曲总秒数的列表
name_list = []  # 用于存储歌曲名称

"""所有需要修改的参数"""
jing_du = 2  # 歌曲相差的间隔

path_list = [r'E:\20231006资料统一\音乐\已经去iTunes', r'E:\20231006资料统一\音乐\MP3COpy\Music',
             r'E:\20231006资料统一\音乐\MP3COpy\新建文件夹1', r'E:\20231006资料统一\音乐\MP3COpy\新建文件夹 (3)',
             r'E:\20231006资料统一\音乐\准备去ituns(封面)', r'E:\20231006资料统一\音乐\林俊杰', r'E:\20231006资料统一\音乐\周杰伦']


def self_alg(numbers, target_sum):
    """思路:
    目标值与最小值或者最大值的差如果小于10秒,可认为算法结束
    首先判断目标数字是否在最大和最小之间
    如果小于最小,就用最小;    如果大于最大(这是最常见的状态)试着判断最小和次小的和,看看结果
    ----【【【【【receive统一返回下标索引】】】】】----
    """
    gap_num = 10  # 两首歌曲间的差值
    return_list = []  # 最终返回的索引列表
    if min(numbers) < target_sum < max(numbers):  # 在最大和最小之间
        print('min(numbers) < target_sum < max(numbers)')
        one_song_alg()
        exit()

    if min(numbers) > target_sum:  # 比最小还小
        print('min(numbers) > target_sum\n')  # 提示视频过短
        return_list.append(numbers.index(min(numbers)))
        return return_list  # 返回目标值的索引

    if max(numbers) < target_sum:  # 比最大还大【主要情况】
        if 2 * min(numbers) > target_sum:  # 小于两倍最小值
            print('2 * min(numbers) > target_sum\n')  # 提示视频过短
            return_list.append(numbers.index(min(numbers)))  # 虽然比最大的大,但是比两个最小的还小,那么可以
            return_list.append(numbers.index(min(numbers)))
            return return_list

        if 2 * min(numbers) < target_sum < 2 * max(numbers):  # 大于两倍最小值并且小于两倍最大值
            print('2 * min(numbers) < target_sum < 2 * max(numbers)')
            temp_list = []  # 存放两首歌各自长度的列表
            sum_list = []  # 计算上面两首歌长度之和
            for i in range(len(numbers)):
                for j in range(i, len(numbers)):
                    if abs(target_sum - numbers[i] - numbers[j]) <= gap_num:
                        temp_list.append([i, j])  # 两首歌的长度的下标索引的存储列表
                        sum_list.append(abs(target_sum - numbers[i] - numbers[j]))  # 列表里面存在与目标值的距离
            # 接下来在歌曲长度列表中查找合适的最小值:
            for ll in sum_list:  # 选取差值最小
                if ll < min(sum_list) + gap_num and temp_list[sum_list.index(ll)] not in return_list:  # 误差内,且没加过
                    return_list.append(temp_list[sum_list.index(ll)])
            return return_list  # 取最后一次满足条件的

        if 2 * max(numbers) < target_sum:
            print('2 * max(numbers) < target_sum')
            return_list.append(numbers.index(max(numbers)))  # 虽然比最大的大,但是比两个最小的还小,那么可以
            return_list.append(numbers.index(max(numbers)))
            return return_list


def list_alg_paixu(path_list1):
    """
    本函数算法主要内容:每10秒1列表,最大、小作差,//10得出长度37左右,按照长度的索引来添加歌曲长度,对所有的歌曲这样遍历一遍。
    第一个for循环,把所有歌曲的时长,名称添加到全局列表中
    """
    """下面先是旧的获取歌曲时长,然后到所有的flac和mp3都能获取时长"""
    for list1 in path_list1:  # 主要在这里获取歌曲长度
        for index, elements in enumerate(os.listdir(list1)):
            duration = TinyTag.get(list1 + '\\' + elements).duration
            len_list.append(int(duration))
            name_list.append(elements)

    qufeng_len_list = []  # 存的是同10秒内歌曲的下标,到用的时候就显示除前后一个列表
    range_len_list = (max(len_list) - min(len_list))//10 + 1  # 这里必须得加一,别忽略了
    for i in range(range_len_list):
        qufeng_len_list.append([])  # 划分这么多个空列表,每10秒1列表
    for index_i, elements_i in enumerate(len_list):
        temp_range = (elements_i-min(len_list))//10
        qufeng_len_list[temp_range].append(index_i)
    return qufeng_len_list


def alg3_selece_appropriate(my_receive):
    """
    my_receive长这样 [[0, 2], [0, 3], [0, 4], [0, 6], [0, 11], [0, 12], [0, 15], [0, 20], [0, 36], [0, 171]],算法找到的相似长度的歌曲
    第一个for循环是把上面这样的二维列表拉成一维的列表
    接着把receive列表里面的元素循环一遍,把该元素所在同10s的列表的前中后都加入合适的列表,接着拉成一维
    判断两首歌与目标时间之差的绝对值是否等于目标精度(目前是把精度也升序循环了一遍)
    最后输出结果,打算倒着来,因为最先看到的一般在最后,那么精度最好的在最后
    """
    temp_receive = []
    try:
        for j in my_receive:
            for i in j:
                if i not in temp_receive:
                    temp_receive.append(i)
        print(temp_receive)
    except TypeError:
        for j in my_receive:
            if j not in temp_receive:
                temp_receive.append(j)

    last_len_list = []  # 存相似长度的歌曲,二维列表,里面的其中一个是相似度十分接近的
    for i in temp_receive:
        for total_j in range(len(same_qufeng_len_list)):
            if i in same_qufeng_len_list[total_j]:
                try:
                    last_len_list.append(same_qufeng_len_list[total_j])  # 相似长度的索引列表存进去了,这里其实可以添加前后的,但是担心太多算不过来,以后【改进】(只要前后加进去,必找到最合适的)
                    last_len_list.append(same_qufeng_len_list[total_j - 1])
                    last_len_list.append(same_qufeng_len_list[total_j + 1])
                except IndexError:
                    pass
    # print(last_len_list)

    take_list = []  # 把上面元素相近的所有元素都取出来
    result_list = []  # 最优解
    for i in last_len_list:
        for j in i:
            if j not in take_list:
                take_list.append(j)
    for i in range(len(take_list)):
        for j in range(i, len(take_list)):
            if abs(len_list[i] + len_list[j] - target_num) < jing_du:
                result_list.append([len_list[i] + len_list[j], name_list[i], name_list[j]])

    # 最后输出结果
    result_count = 0
    for p in range(jing_du - 1, -1, -1):
        print('\n\n\n精度:  ', p)
        for i in result_list:
            if abs(i[0] - target_num) == p:
                print('%d\t %-25s \t %-20s' % (i[0], i[1], i[2]))
                # print(i[0], i[1], '\t', i[2])
                result_count += 1
    print('\n共找到{}个结果!'.format(result_count))
    input('Press any key to exit.')


def one_song_alg():
    """
    如果只有一首歌,那么就调用这个函数,返回在精度误差内的歌曲。
    """
    index_list = []
    for i in range(len(len_list)):
        if abs(len_list[i] - target_num) < jing_du and i not in index_list:
            # print(len_list[i], name_list[i])
            index_list.append(i)

    for j in range(jing_du):  # 重复是难免的,因为旧mp3中有许多现在也有的,下标算法没问题,是歌曲重复了。
        for i in index_list:
            if abs(len_list[i] - target_num) == j:
                print(len_list[i], name_list[i])
        print('')


if __name__ == '__main__':
    target_num = int(input("请输入总音乐的秒数:"))  # 视频长度的秒数

    same_qufeng_len_list = list_alg_paixu(path_list)

    receive = self_alg(len_list, target_num)  # 默认receive里面都是返回的视频的时长的 索引下标!!! 求时长还是len_list[i]
    if len(receive) == 2:
        print(len_list[receive[0]], name_list[receive[0]])
        print(len_list[receive[1]], name_list[receive[1]])
        
    alg3_selece_appropriate(receive)  # 最主要算法3,整合receive和时长相近的歌曲,最后输出结果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值