Giter VIP home page Giter VIP logo

xhongc / music-tag-web Goto Github PK

View Code? Open in Web Editor NEW
1.8K 8.0 109.0 32.97 MB

音乐标签编辑器,可编辑本地音乐文件的元数据(Editable local music file metadata.)

Home Page: https://xiers-organization.gitbook.io/music-tag-web-v2

License: GNU General Public License v3.0

Python 46.10% Dockerfile 0.34% HTML 19.16% JavaScript 15.88% Vue 6.99% SCSS 1.56% CSS 9.91% Shell 0.07%
music-tag navidrome mp3tag music tag musictag docker nas

music-tag-web's Introduction

🚀 Music Tag Web

『音乐标签』Web版是一款可以编辑歌曲的标题,专辑,艺术家,歌词,封面等信息的音乐标签编辑器程序, 支持FLAC, APE, WAV, AIFF, WV, TTA, MP3, M4A, OGG, MPC, OPUS, WMA, DSF, DFF, MP4等音频格式。

docker-pull-count docker-platform

🎉 Feature

为什么开发web版? 在使用Navidrome时,我的音乐都是在远程服务器上的,本地的Musictag和mp3tag不能满足我的需求, 我需要部署在远程服务器上去需改线上的音乐标签,相当于在使用Navidrome的边车应用。

  • 支持大部分音频格式元数据的查看、编辑和修改
  • 支持批量自动修改(刮削)音乐标签
  • 支持音乐指纹识别,即使没有元数据也可以识别音乐
  • 支持整理音乐文件,按艺术家,专辑分组, 或者自定义多级分组
  • 支持文件排序,按照文件名,文件大小,更新时间排序
  • 支持批量转换音乐元数据繁体转简体,或者简体转繁体
  • 支持文件名称的拆分解包,补充缺失元数据信息
  • 支持文本替换,批量替换音乐元数据中脏数据
  • 支持音乐格式转换,引入 ffmpeg 支持音乐格式转换
  • 支持整轨音乐文件的切割
  • 支持多种音乐标签来源
  • 支持歌词翻译功能
  • 支持显示操作记录
  • 支持导出专辑封面文件,支持自定义上传专辑封面
  • 支持适配移动端 UI,支持手机端访问
  • 支持使用小爱同学播放本地音乐,播放NAS本地音乐

🦀 Show Project

DEMO 地址账号密码为:admin/admin

【音乐标签Web|Music Tag Web】

🔨 How to Build?

1. docker-compose -f local.yml build
2. docker-compose -f local.yml up

💯 How to Use

【使用手册】

【使用手册V2】

V2的部署方式 使用手册 V2的方式部署!!

V1部署方式

镜像已上传至Dockerhub 操作指南:

1.从Docker Hub拉取镜像

docker pull xhongc/music_tag_web:latest

2. 运行容器镜像

docker run -d -p 8001:8001 -v /path/to/your/music:/app/media -v /path/to/your/config:/app/data --restart=always xhongc/music_tag_web:latest

或者 使用portainer Stacks部署(docker compose) img_1.png

version: '3'

services:
  music-tag:
    image: xhongc/music_tag_web:latest
    container_name: music-tag-web
    ports:
      - "8001:8001"
    volumes:
      - /path/to/your/music:/app/media:rw
      - /path/to/your/config:/app/data
    command: /start
    restart: unless-stopped

ps. /path/to/your/music 改成你的音乐文件夹路径!/path/to/your/config 改为配置文件路径!

3 访问在127.0.0.1:8001/admin 默认账号密码 admin/admin 修改默认密码 img_7.png

V2部署方式

与 V1部署的区别是容器内端口改为 8002, Docker Compose 部署去掉了 command: /start 配置

1.从Docker Hub拉取镜像

docker pull xhongc/music_tag_web:latest

2. 运行容器镜像

docker run -d -p 8002:8002 -v /path/to/your/music:/app/media -v /path/to/your/config:/app/data --restart=always xhongc/music_tag_web:latest

或者:

version: '3'

services:
  music-tag:
    image: xhongc/music_tag_web:latest
    container_name: music-tag-web
    ports:
      - "8002:8002"
    volumes:
      - /path/to/your/music:/app/media:rw
      - /path/to/your/config:/app/data
    restart: unless-stopped

ps. /path/to/your/music 改成你的音乐文件夹路径!/path/to/your/config 改为配置文件路径!

3 访问在127.0.0.1:8002/admin 默认账号密码 admin/admin 修改默认密码

📷 User Interface

img_5.png img_4.png img_11.png img_12.png img_2.png

💬 Contact me

如有什么意见需求,请先 star 后提出issues,我会满足你的需求,在我的能力范围内。 issue 没及时看到的,可以加群讨论!(已经满200人,现在进群需手动拉,请添加我微信(备注:Music Tag),我会拉你进群。)

 
## 发布频道:

t.me/music_tag_web

MusicTag 交流群

QQ群:55893996

💸 赞助与支持

如果您觉得 music-tag-web 对你有帮助,可以请作者喝杯咖啡。 您的支持是我们更新软件的动力, 谢谢您! (。・∀・)ノ゙

➡ 爱发电

   

🌟 Star History

Star History Chart

免责声明

禁止任何形式的商业用途,包括但不仅限于售卖/打赏/获利,不得使用本代码进行任何形式的牟利/贩卖/传播,再次强调仅供个人私下研究学习技术使用,不提供下载音乐本体! 本项目仅以纯粹的技术目的去学习研究,如有侵犯到任何人的合法权利,请致信[email protected],我将在第一时间修改删除相关代码,谢谢!

本项目基于 GPL V3.0 许可证发行,以下协议是对于 GPL V3.0 的补充,如有冲突,以以下协议为准。

词语约定:本协议中的“本项目”指music-tag-web项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本项目内置的包括酷我、网易云、QQ音乐、咪咕、酷狗音乐、酷我音乐等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字、歌词等在内的他人拥有所属版权的数据。

本项目的数据来源原理是从各官方音乐平台的公开服务器中拉取数据,经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的准确性负责。 使用本项目的过程中可能会产生版权数据,对于这些版权数据,本项目不拥有它们的所有权,为了避免造成侵权,使用者务必在24小时内清除使用本项目的过程中所产生的版权数据。 本项目内的官方音乐平台别名为本项目内对官方音乐平台的一个称呼,不包含恶意,如果官方音乐平台觉得不妥,可联系本项目更改或移除。 本项目内使用的部分包括但不限于字体、图片等资源来源于互联网,如果出现侵权可联系本项目移除。 由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。 本项目完全免费,仅供个人私下小范围研究交流学习 python 技术使用, 且开源发布于 GitHub 面向全世界人用作对技术的学习交流,本项目不对项目内的技术可能存在违反当地法律法规的行为作保证,禁止在违反当地法律法规的情况下使用本项目,对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。 若你使用了本项目,将代表你接受以上协议。

music-tag-web's People

Contributors

xhongc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

music-tag-web's Issues

建议搜索的时候同时搜索歌名和艺术家

由于现在翻唱的太多,建议搜索同时对歌名和艺术家进行搜索,这样出来的信息更准确。今天使用批量修改的功能,发现很多歌曲因为翻唱在前,填充的信息是错误的。

大佬你好,能否开放一个自己加 tag的自定义字段

你好:
我是用plex用户,plex在艺术家页面会按普通专辑,ep,live 等分类。但普通tag并无专辑分类字段,而plex 是读取Muiscbrainz的分类,如果Muiscbrainz没有专辑分类则无效,需要自己在Muiscbrainz提交分组,十分不便,或者自己修改文件tag【加字段】来分组。
相应软件是可以设置,不过我的音乐均存在服务器本地修改会非常慢。如果music tag web 能有这个功能就好了。
当然如果可以直接有一个字段(plex里用RELEASETYPE【FLAC】,MusicBrainz Album Type【MP3】),
而plex 该字段有5个识别类型album;compilation【合辑】,album;live【现场】,ep【单曲或ep】,album;soundtrack【原声】,以及无该字段则为普通专辑,在批量修改文件时能选择专辑类型是否可以实现?不修改则为普通专辑。

多艺术家支持及id3v1编码问题

image
左侧为修改后文件,右侧为原文件。
1.不知何故,修改后id3v1的汉字全部为乱码。
2.很多歌曲是多艺术家的,建议增加多艺术家支持。

之前自定义标签提供有误

Snipaste_2023-08-09_13-46-33
是“MusicBrainz Album Type”【测试通过】,而非“MusicBrainzAlbumType”【测试无法生效】,单词之间有空格,之前表达有误,向大佬报告下这个情况。

移动端支持

希望添加移动端支持。使用现有的网页,比如demo在手机浏览器上打开时显示是正常的。
但是当点击输入框或者选择刮削源后,网页比例就会变形。

[Feat] 请增加歌词下载

一键(批量)将歌词保存为 <文件名>.lrc 便于本地需要通过 lrc 匹配的软件进行识别

现在好像自动批量还没实现?是禁用状态。

最后,非常感谢你的项目,macOS 平台,华语音乐的刮削还得是你的项目好用!

希望和建议

希望docker镜像可以出个纯净版本的 只保留music-tag本身 去掉nginx、mysql、redis
起因是我本地有这些乱七八糟的

我这里是使用的系统环境变量

DATABASE_ENGINE="django.db.backends.mysql"
DATABASE_NAME="music"
DATABASE_USER="root"
DATABASE_PASSWORD="123456"
DATABASE_HOST="192.168.1.220"
DATABASE_PORT="3306"

REDIS_URI="[email protected]:6379/0"

settings.py

......

DATABASES = {
    "default": {
        "ENGINE": os.getenv("DATABASE_ENGINE"),
        "NAME": os.getenv("DATABASE_NAME"),  # noqa
        "USER": os.getenv("DATABASE_USER"),
        "PASSWORD": os.getenv("DATABASE_PASSWORD"),
        "HOST": os.getenv("DATABASE_HOST"),
        "PORT": os.getenv("DATABASE_PORT"),
    },
}

......

BROKER_URL = f"redis://{os.getenv('REDIS_URI')}"

......

然后就是'保存歌词lrc:'那里 要点下开关 不然会报错
serialziers.py

class MusicId3Serializer(serializers.Serializer):
    is_save_lyrics_file = serializers.BooleanField(required=False)

还有一个是mysql的问题
我在本地要在django_vue_cli/__init__.py文件中增加

import pymysql
pymysql.install_as_MySQLdb()

不然会报错

django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?

在刮削的时候指定了 专辑名称 还是会刮削到别的

在刮削的时候指定了 专辑名称 还是会刮削到别的,
image
如图 这样,在什么信息都没有的时候是否可以使用自动批量修改?
是需要先手动还是需要指定什么信息才可以批量刮削?
这个艺术家名称和专辑艺术家,到底是那个属性对应右边框里面的歌手。
image
是要 把信息填写好在点自动批量修改吗?
文件名中的 ${title}-${album} 要怎么使用,能有个文档说明么?
image
就是这里说是歌手 但是 左边框里面的艺术家和 专辑艺术家到底取的哪一个值?
image

希望大佬能出一个使用教程。有点蒙.必须支持

mac m1 运行报错

捕获未处理异常,异常具体堆栈->[Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/rest_framework/viewsets.py", line 103, in view
return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 45, in _wrapper
return bound_method(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 483, in dispatch
response = self.handle_exception(exc)
File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 443, in handle_exception
self.raise_uncaught_exception(exc)
File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 480, in dispatch
response = handler(request, *args, **kwargs)
File "/app/applications/task/views.py", line 196, in batch_auto_update_id3
batch_auto_tag_task(timestamp, source_list, select_mode)
File "/app/applications/task/tasks.py", line 270, in batch_auto_tag_task
is_match = match_song(resource, task.full_path, select_mode)
File "/app/applications/task/utils.py", line 42, in match_song
songs = MusicResource(resource).fetch_id3_by_title(title)
File "/app/applications/task/services/music_resource.py", line 32, in fetch_id3_by_title
return self.resource.fetch_id3_by_title(title)
File "/app/applications/task/services/kugou.py", line 204, in fetch_id3_by_title
signature = getSignature(p)
File "/app/applications/task/services/kugou.py", line 187, in getSignature
js_obj = execjs.compile(js_str)
File "/usr/local/lib/python3.9/site-packages/execjs/init.py", line 61, in compile
return get().compile(source, cwd)
File "/usr/local/lib/python3.9/site-packages/execjs/_runtimes.py", line 21, in get
return get_from_environment() or _find_available_runtime()
File "/usr/local/lib/python3.9/site-packages/execjs/_runtimes.py", line 49, in _find_available_runtime
raise exceptions.RuntimeUnavailableError("Could not find an available JavaScript runtime.")
execjs._exceptions.RuntimeUnavailableError: Could not find an available JavaScript runtime.

抱歉不知道您还需要什么信息,先贴上 docker 日志

error

stderr: File "/usr/local/lib/python3.9/importlib/init.py", line 127, in import_module
stderr: return _bootstrap._gcd_import(name[level:], package, level)
stderr: File "", line 1030, in _gcd_import
stderr: File "", line 1007, in _find_and_load
stderr: File "", line 986, in _find_and_load_unlocked
stderr: File "", line 680, in _load_unlocked
stderr: File "", line 850, in exec_module
stderr: File "", line 228, in _call_with_frames_removed
stderr: File "/app/django_vue_cli/urls.py", line 5, in
stderr: from applications.task.urls import router as task_router
stderr: File "/app/applications/task/urls.py", line 3, in
stderr: from . import views
stderr: File "/app/applications/task/views.py", line 17, in
stderr: from applications.utils.translation import translation_lyc_text
stderr: File "/app/applications/utils/translation.py", line 8, in
stderr: from component import translators as ts
stderr: File "/app/component/translators/init.py", line 5, in
stderr: from .server import translate_text, translate_html, translators_pool, get_languages, preaccelerate_and_speedtest
stderr: File "/app/component/translators/server.py", line 5310, in
stderr: tss = TranslatorsServer()
stderr: File "/app/component/translators/server.py", line 5025, in init
stderr: self.server_region = GuestSeverRegion().get_server_region
stderr: File "/app/component/translators/server.py", line 328, in get_server_region
stderr: raise TranslatorError('Unable to connect the Internet.\n\n')
stderr: component.translators.server.TranslatorError: Unable to connect the Internet.
stderr:
stderr:

请问有没有登录用户和密码功能呀

因为好像直接访问就能进去了,但是因为是有公网ip的,怕别人登录上去乱搞,所以想问问是不是有需要用户名和密码才能登录刮削界面的功能?

希望作者添加或者优化自动获取歌曲信息批量修改,多加QQ音乐接口

感谢作者的开源项目,但是需要手动一个个点击添加歌词,自动批量修改不能使用。我根据查询Navidrome的数据库来调用项目的接口实现了自动化,我希望作者也能添加自动化封装歌词。同时多加QQ音乐接口,网易接口有一些歌曲识别没有。
这是我随便写的Java批量代码

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@SpringBootTest
class MusicApplicationTests {

    @Test
    void contextLoads() {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        //链接db
        try {
            // 加载 SQLite 驱动程序
            Class.forName("org.sqlite.JDBC");

            // 连接到 SQLite 数据库
            conn = DriverManager.getConnection("jdbc:sqlite:C:\\Users\\admin\\Desktop\\navidrome.db");

            // 创建 Statement 对象
            stmt = conn.createStatement();

            // 执行查询语句
            String sql = "SELECT path FROM media_file";
            rs = stmt.executeQuery(sql);
            // 将结果存储在列表中
            List<String> coverArtPaths = new ArrayList<>();
            // 遍历结果并打印 cover_art_path 字段的值
            while (rs.next()) {
                String coverArtPath = rs.getString("path");
						//	把Navidrome音乐的地址替换为本项目的音乐地址
                String replacedOutput = coverArtPath.replace("/music/", "/app/media//");
                coverArtPaths.add(replacedOutput);
            }
            for (int i = 1938; i < coverArtPaths.size(); i++) {
                String coverArtPathsList = coverArtPaths.get(i);
                System.out.println("歌曲顺序"+i);
                JSONObject jsonObject = new JSONObject();
                // 使用 File 类来处理路径
                File file = new File(coverArtPathsList);
                // 获取文件名
                String fileName = file.getName();
                // 获取目录部分
                int lastIndex = coverArtPathsList.lastIndexOf("/");
                String directory = coverArtPathsList.substring(0, lastIndex + 1);
                jsonObject.put("file_path",directory);
                jsonObject.put("file_name",fileName);


                System.out.println(jsonObject);
                //                jsonObject.put()
                HttpResponse<String> musicInfo = Unirest.post("http://baidu.com/api/music_id3/")
                        .header("Content-Type", "application/json")
                        .body(jsonObject.toString())
                        .asString();
                String body = musicInfo.getBody();
                JSONObject jsonMusicInfo = JSONObject.parseObject(body);
                JSONObject jsonMusicInfoData = jsonMusicInfo.getJSONObject("data");
                if (jsonMusicInfoData!=null){
                    System.out.println(jsonMusicInfo);
                    String lyrics = jsonMusicInfoData.getString("lyrics");
                    // "lyrics": ""
                    if (Objects.isNull(lyrics) || lyrics.isEmpty()){
                        System.out.println("歌词为空");
                        //搜索歌曲信息
                        JSONObject jsonMusicInfoSearch = new JSONObject();
                        String title = jsonMusicInfoData.getString("title");
                        jsonMusicInfoSearch.put("title",title);
                        jsonMusicInfoSearch.put("resource","netease");
                        System.out.println("要搜索的信息:"+jsonMusicInfoSearch);
                        HttpResponse<String> musicInfoSearch = Unirest.post("http://baidu.com/api/fetch_id3_by_title/")
                                .header("Content-Type", "application/json")
                                .body(jsonMusicInfoSearch.toString())
                                .asString();
                        String musicInfoSearchBody = musicInfoSearch.getBody();
                        JSONObject jsonObjectmusicInfo= JSONObject.parseObject(musicInfoSearchBody);
                        System.out.println("搜索到的结果:"+musicInfoSearchBody);
                        JSONArray jsonObjecmusicInfoSearchData = jsonObjectmusicInfo.getJSONArray("data");
                        //遍历找出完全匹配的数据
                        //读取的歌词专辑名称
                        String album = jsonMusicInfoData.getString("album");
                        //读取的歌曲作者
                        String artist = jsonMusicInfoData.getString("artist");
                        jsonObjecmusicInfoSearchData.stream().forEach(s->{
                            String s1 = s.toString();
                            JSONObject jsonObjects = JSONObject.parseObject(s1);
                            //结果的专辑名称
                            String albums = jsonObjects.getString("album");
                            //结果的歌曲名称
                            String names = jsonObjects.getString("name");
                            //结果的歌曲作者
                            String artists = jsonObjects.getString("artist");
                            if (title.equals(names)&&artist.equals(artists)){
                                System.out.println("完全匹配搜索结果:"+s);
                                JSONObject jsonObjectLyric = new JSONObject();
                                Integer id = jsonObjects.getInteger("id");
                                jsonObjectLyric.put("song_id",id);
                                jsonObjectLyric.put("resource","netease");
                                HttpResponse<String> lyric = Unirest.post("http://baidu.com/api/fetch_lyric/")
                                        .header("Content-Type", "application/json")
                                        .body(jsonObjectLyric.toJSONString())
                                        .asString();
                                String bodyLyric = lyric.getBody();
                                JSONObject jsonLyric = JSONObject.parseObject(bodyLyric);
                                String dataLyric = jsonLyric.getString("data");
                                System.out.println("匹配到歌词");
                                //组装更新歌曲信息更新
                                JSONArray jsonArrayMusicup = new JSONArray();
                                JSONObject jsonMusicup = new JSONObject();
                                jsonMusicup.put("file_full_path",coverArtPathsList);
                                jsonMusicup.put("title",names);
                                jsonMusicup.put("artist",artists);
                                jsonMusicup.put("album",album);
                                //读取的歌曲类型
                                String genre = jsonMusicInfoData.getString("genre");
                                jsonMusicup.put("genre",genre);
                                String year = jsonMusicInfoData.getString("year");
                                jsonMusicup.put("year",year);
                                jsonMusicup.put("lyrics",dataLyric);
                                //读取的歌曲comment
                                String comment = jsonMusicInfoData.getString("comment");
                                jsonMusicup.put("comment",comment);
                                //读取的歌曲comment
                                String artwork = jsonMusicInfoData.getString("artwork");
                                jsonMusicup.put("artwork",artwork);
                                jsonMusicup.put("filename",fileName);
                                //保存歌词lrc
                                jsonMusicup.put("is_save_lyrics_file",false);
                                //专辑封面
                                String album_img = jsonMusicInfoData.getString("album_img");
                                jsonMusicup.put("album_img",album_img);

                                jsonArrayMusicup.add(jsonMusicup);
                                JSONObject jsonObject1 = new JSONObject();
                                jsonObject1.put("music_id3_info",jsonArrayMusicup);
                                HttpResponse<String> musicup = Unirest.post("http://baidu.com/api/update_id3/")
                                        .header("Content-Type", "application/json")
                                        .body(jsonObject1.toString())
                                        .asString();
                                String musicupbody = musicup.getBody();
                                JSONObject jsonObject2 = JSONObject.parseObject(musicupbody);
                                if ("200".equals(jsonObject2.getString("code"))){
                                    System.out.println("歌词更新成功:"+musicupbody);
                                }else {
                                    System.out.println("歌词更新失败"+musicupbody);
                                }
                                try {
                                    Thread.sleep(1000); // 等待1秒
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }


                            }
                        });

                    }else {
                        System.out.println("歌词不为空");
                    }
                }else {
                    System.out.println("搜索不到歌词");
                }
//                Thread.sleep(3000);
//                System.out.println("等待3秒");
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭 ResultSet、Statement 和 Connection
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}


说一下几点问题

首先不得不说docker部署很实用,体验也挺好的,不过还是遇到几点问题
1.不能修改和设置密码
2.部署创建以后会多出music文件夹(虽然可以删除不影响)
3.最好就是可以定时自动扫描音乐情况,可以自动匹配,而无需手动去选择再批量自动匹配
4.音乐分类更加倾向于按语言分类(国语,日语,英语)这样,自动整理好目录
5.上面提到的3,其实最好给一个专门目录,整理过的分类好,没整理的一个目录,这样就知道那些有没有整理了
6.显示信息可以切换为常规的列表这样比较好一次看里面信息,现在需要一个个点才能显示

反馈问题,mp3专辑类型无法保持,FLAC可以

感谢大佬.更新神速啊。

专辑类型测试了下,FLAC修改专辑类型可以修改,MP3修改后保存出错。
测试了不同的专辑里的mp3均不能保存,FLAC均可以。不知道哪里出了问题。

[功能需求] 获取音乐平台提供的歌词译文

首先想要感谢开发者,我也是用 Navidrome 搭建的个人音乐服务,这款应用真的帮了我很多。

关于 #18 提到的歌词翻译问题的补充,希望开发者能够增加 获取音乐平台提供的歌词译文 的功能。例如在QQ音乐上 https://y.qq.com/n/ryqq/songDetail/000g0uA94RBYYa 这是一首日语歌,并且QQ音乐平台提供了歌词的译文(如下图)。
[QQ音乐平台截图]

希望开发者能支持这个功能,就像上图中这样一行原文一行译文。另外,有一个开源项目(jitwxs/163MusicLyrics)实现了这个功能,并且实现了网易云音乐和QQ音乐的API,希望能给开发者提供参考。
image

自动批量修改无效

使用自动批量修改没有保存信息
image
日志也是正常的
image
请大佬解答下是我姿势不对还是需要什么参数吗?万分感激

IOS

可以开发一下ios端的音乐标签吗,这个网页版的好像不好搜索苹果文件里的音乐,我还是挺期待苹果可以使用这个网站的

CD号功能能否提供

首选感谢您提供这么好得程序,我也是用navidrome建音乐课放在远程服务端,用MP3tag等搜刮实在速度太慢。
用music tag web真得速度非常快也非常好用。
我有很多多碟得音乐,如果能提供CD号修改就太好了。
还有一个我发现某些乱码的wav文件,标签虽然能修改,却在navidrome里被识别为乱码。
再次感谢提供如此好的应用。谢谢

关于docker部署之后报错

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 217, in ensure_connection
    self.connect()
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 195, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/mysql/base.py", line 227, in get_new_connection
    return Database.connect(**conn_params)
  File "/usr/local/lib/python3.9/site-packages/MySQLdb/__init__.py", line 84, in Connect
    return Connection(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/MySQLdb/connections.py", line 166, in __init__
    super(Connection, self).__init__(*args, **kwargs2)
MySQLdb._exceptions.OperationalError: (2002, "Can't connect to MySQL server on '127.0.0.1' (115)")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/app/manage.py", line 22, in <module>
    main()
  File "/app/manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 361, in execute
    self.check()
  File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 387, in check
    all_issues = self._run_checks(
  File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 64, in _run_checks
    issues = run_checks(tags=[Tags.database])
  File "/usr/local/lib/python3.9/site-packages/django/core/checks/registry.py", line 72, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/usr/local/lib/python3.9/site-packages/django/core/checks/database.py", line 10, in check_database_backends
    issues.extend(conn.validation.check(**kwargs))
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/mysql/validation.py", line 9, in check
    issues.extend(self._check_sql_mode(**kwargs))
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/mysql/validation.py", line 13, in _check_sql_mode
    with self.connection.cursor() as cursor:
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 256, in cursor
    return self._cursor()
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 233, in _cursor
    self.ensure_connection()
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 217, in ensure_connection
    self.connect()
  File "/usr/local/lib/python3.9/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 217, in ensure_connection
    self.connect()
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 195, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/mysql/base.py", line 227, in get_new_connection
    return Database.connect(**conn_params)
  File "/usr/local/lib/python3.9/site-packages/MySQLdb/__init__.py", line 84, in Connect
    return Connection(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/MySQLdb/connections.py", line 166, in __init__
    super(Connection, self).__init__(*args, **kwargs2)
django.db.utils.OperationalError: (2002, "Can't connect to MySQL server on '127.0.0.1' (115)")

应该是sql连接问题

自动化任务功能

大佬您好,这个项目的功能正是刚需,这边有个小小的需求,就是可以增加自动话任务,例如监测目录内容变化,触发自动任务,自动进行整理任务。
大佬如果有交流群的话,可否申请加入

我这边启动失败提示Unable to connect the Internet

2023-08-23 22:49:28 stdout self.server_region = GuestSeverRegion().get_server_region
2023-08-23 22:49:28 stdout File "/app/component/translators/server.py", line 328, in get_server_region
2023-08-23 22:49:28 stdout raise TranslatorError('Unable to connect the Internet.\n\n')
2023-08-23 22:49:28 stdout component.translators.server.TranslatorError: Unable to connect the Internet.
2023-08-23 22:49:28 stdout  
2023-08-23 22:49:28 stdout  
2023-08-23 22:49:28 stdout tss = TranslatorsServer()
2023-08-23 22:49:28 stdout File "/app/component/translators/server.py", line 5025, in init
2023-08-23 22:49:28 stdout File "/app/django_vue_cli/urls.py", line 5, in
2023-08-23 22:49:28 stdout from applications.task.urls import router as task_router
2023-08-23 22:49:28 stdout File "/app/applications/task/urls.py", line 3, in
2023-08-23 22:49:28 stdout from . import views
2023-08-23 22:49:28 stdout File "/app/applications/task/views.py", line 21, in
2023-08-23 22:49:28 stdout from applications.utils.translation import translation_lyc_text
2023-08-23 22:49:28 stdout File "/app/applications/utils/translation.py", line 8, in
2023-08-23 22:49:28 stdout from component import translators as ts
2023-08-23 22:49:28 stdout File "/app/component/translators/init.py", line 5, in
2023-08-23 22:49:28 stdout from .server import translate_text, translate_html, translators_pool, get_languages, preaccelerate_and_speedtest
2023-08-23 22:49:28 stdout File "/app/component/translators/server.py", line 5310, in
2023-08-23 22:49:28 stdout all_namespaces = _load_all_namespaces(resolver)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/checks/urls.py", line 57, in _load_all_namespaces
2023-08-23 22:49:28 stdout url_patterns = getattr(resolver, 'url_patterns', [])
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 80, in get
2023-08-23 22:49:28 stdout res = instance.dict[self.name] = self.func(instance)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/urls/resolvers.py", line 584, in url_patterns
2023-08-23 22:49:28 stdout patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 80, in get
2023-08-23 22:49:28 stdout res = instance.dict[self.name] = self.func(instance)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/urls/resolvers.py", line 577, in urlconf_module
2023-08-23 22:49:28 stdout return import_module(self.urlconf_name)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/importlib/init.py", line 127, in import_module
2023-08-23 22:49:28 stdout return _bootstrap._gcd_import(name[level:], package, level)
2023-08-23 22:49:28 stdout File "", line 1030, in _gcd_import
2023-08-23 22:49:28 stdout File "", line 1007, in _find_and_load
2023-08-23 22:49:28 stdout File "", line 986, in _find_and_load_unlocked
2023-08-23 22:49:28 stdout File "", line 680, in _load_unlocked
2023-08-23 22:49:28 stdout File "", line 850, in exec_module
2023-08-23 22:49:28 stdout File "", line 228, in _call_with_frames_removed
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/management/init.py", line 381, in execute_from_command_line
2023-08-23 22:49:28 stdout utility.execute()
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/management/init.py", line 375, in execute
2023-08-23 22:49:28 stdout self.fetch_command(subcommand).run_from_argv(self.argv)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 323, in run_from_argv
2023-08-23 22:49:28 stdout self.execute(*args, **cmd_options)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 361, in execute
2023-08-23 22:49:28 stdout self.check()
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 387, in check
2023-08-23 22:49:28 stdout all_issues = self._run_checks(
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 65, in _run_checks
2023-08-23 22:49:28 stdout issues.extend(super()._run_checks(**kwargs))
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 377, in _run_checks
2023-08-23 22:49:28 stdout return checks.run_checks(**kwargs)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/checks/registry.py", line 72, in run_checks
2023-08-23 22:49:28 stdout new_errors = check(app_configs=app_configs)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/django/core/checks/urls.py", line 40, in check_url_namespaces_unique
2023-08-23 22:49:28 stdout urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='geolocation.onetrust.com', port=443): Max retries exceeded with url: /cookieconsentpub/v1/geo/location (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f89bad7fb80>: Failed to establish a new connection: [Errno 111] Connection refused'))
2023-08-23 22:49:28 stdout  
2023-08-23 22:49:28 stdout During handling of the above exception, another exception occurred:
2023-08-23 22:49:28 stdout  
2023-08-23 22:49:28 stdout Traceback (most recent call last):
2023-08-23 22:49:28 stdout File "/app/component/translators/server.py", line 317, in get_server_region
2023-08-23 22:49:28 stdout data = json.loads(requests.get(self.get_addr_url, headers=_headers_fn(self.get_addr_url)).text[9:-2])
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/requests/api.py", line 73, in get
2023-08-23 22:49:28 stdout return request("get", url, params=params, **kwargs)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/requests/api.py", line 59, in request
2023-08-23 22:49:28 stdout return session.request(method=method, url=url, **kwargs)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 589, in request
2023-08-23 22:49:28 stdout resp = self.send(prep, **send_kwargs)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 703, in send
2023-08-23 22:49:28 stdout r = adapter.send(request, **kwargs)
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/requests/adapters.py", line 519, in send
2023-08-23 22:49:28 stdout raise ConnectionError(e, request=request)
2023-08-23 22:49:28 stdout requests.exceptions.ConnectionError: HTTPSConnectionPool(host='geolocation.onetrust.com', port=443): Max retries exceeded with url: /cookieconsentpub/v1/geo/location (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f89bad7fb80>: Failed to establish a new connection: [Errno 111] Connection refused'))
2023-08-23 22:49:28 stdout  
2023-08-23 22:49:28 stdout During handling of the above exception, another exception occurred:
2023-08-23 22:49:28 stdout  
2023-08-23 22:49:28 stdout Traceback (most recent call last):
2023-08-23 22:49:28 stdout File "/app/manage.py", line 22, in
2023-08-23 22:49:28 stdout main()
2023-08-23 22:49:28 stdout File "/app/manage.py", line 18, in main
2023-08-23 22:49:28 stdout execute_from_command_line(sys.argv)
2023-08-23 22:49:28 stdout conn.connect()
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 611, in connect
2023-08-23 22:49:28 stdout self.sock = sock = self._new_conn()
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 218, in _new_conn
2023-08-23 22:49:28 stdout raise NewConnectionError(
2023-08-23 22:49:28 stdout urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x7f89bad7fb80>: Failed to establish a new connection: [Errno 111] Connection refused
2023-08-23 22:49:28 stdout  
2023-08-23 22:49:28 stdout The above exception was the direct cause of the following exception:
2023-08-23 22:49:28 stdout  
2023-08-23 22:49:28 stdout Traceback (most recent call last):
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/requests/adapters.py", line 486, in send
2023-08-23 22:49:28 stdout resp = conn.urlopen(
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 844, in urlopen
2023-08-23 22:49:28 stdout retries = retries.increment(
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/urllib3/util/retry.py", line 515, in increment
2023-08-23 22:49:28 stdout raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]
2023-08-23 22:49:28 stdout Traceback (most recent call last):
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 203, in _new_conn
2023-08-23 22:49:28 stdout sock = connection.create_connection(
2023-08-23 22:49:28 stdout File "/usr/local/lib/python3.9/site-packages/urllib3/util/connection.py", line 85, in create_connection
2023-08-23 22:49:28 stdout raise err
。。。

WMA文件无法匹配及批量自动匹配

1.WMA文件无法匹配,会提示ASF format not implemented
2.批量自动匹配是不是只能选择一个文件夹?同时选择多个文件夹或者文件夹中还有文件夹就不能批量
2.1测试了,似乎是一次性选择的音乐文件数量有限制,太多就不能批量匹配

文件排序

当文件或文件夹多的时候,需要排序功能,如时间,名称,大小
图片

点击搜索后无反应

我使用podman创建容器:

podman run -d -p 8001:8001 \
    --name music-tag \
    -v /opt/rclone/TimeCloud:/app/media:z \
    --restart=always \
    docker.io/xhongc/music_tag_web:latest

之后使用http://ip:8001访问服务,页面正常加载,正常登陆。
任意选择一个音乐文件,出现音乐信息后,点击标题编辑框旁边的小箭头,没有反应。
浏览器报错如下:

POST http://ip:8001/api/fetch_id3_by_title/ 500 (Internal Server Error)
(匿名)	@	vendor.051dd49….js:12
e.exports	@	vendor.051dd49….js:12
e.exports	@	vendor.051dd49….js:36
Promise.then(异步)		
XmWM.c.request	@	vendor.051dd49….js:30
c.<computed>	@	vendor.051dd49….js:30
(匿名)	@	vendor.051dd49….js:12
(匿名)	@	app.d34f0d5….js:1
t	@	vendor.051dd49….js:30
S	@	app.d34f0d5….js:1
fetchId3Title	@	app.d34f0d5….js:1
toggleLock	@	app.d34f0d5….js:1
click	@	app.d34f0d5….js:1
$e	@	vendor.051dd49….js:12
n	@	vendor.051dd49….js:12
a._wrapper	@	vendor.051dd49….js:12
TypeError: Cannot read properties of undefined (reading 'length')
    at o.render (app.d34f0d5….js:1:190759)
    at e._render (vendor.051dd49….js:12:35434)
    at gn.before (vendor.051dd49….js:12:68568)
    at gn.get (vendor.051dd49….js:12:26823)
    at gn.run (vendor.051dd49….js:12:27707)
    at pn (vendor.051dd49….js:12:25815)
    at Array.<anonymous> (vendor.051dd49….js:12:12418)
    at Xe (vendor.051dd49….js:12:11818)

容器logs 如下:

[2023-09-08 00:30:49 +0800] [6] [INFO] Booting worker with pid: 6
捕获未处理异常,异常具体堆栈->
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/viewsets.py", line 103, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 45, in _wrapper
    return bound_method(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 483, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 443, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 480, in dispatch
    response = handler(request, *args, **kwargs)
  File "/app/applications/task/views.py", line 233, in fetch_id3_by_title
    songs = MusicResource(resource).fetch_id3_by_title(title)
  File "/app/applications/task/services/music_resource.py", line 38, in fetch_id3_by_title
    return self.resource.fetch_id3_by_title(title)
  File "/app/applications/task/services/music_resource.py", line 51, in fetch_id3_by_title
    songs = data.json().get("result", {}).get("songs", [])
AttributeError: 'str' object has no attribute 'get'

请求URL->[/api/fetch_id3_by_title/]
请求方法->[POST]
请求参数->[{}]

访问是通过vps ip访问的,没有配置反向代理服务器。

[功能需求] 增强对文件的操作

4d1610e47a398ad601d790bfbb9b590

希望在这儿 加个对文件、文件夹的操作
新建文件夹、批量移动文件到文件夹、删除
搜索时"包括子文件夹"的选项

以及对文件名的修改(只能手动修改 从最右侧选中回来时可以根据规则覆盖原有文件名)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.