mirror of
https://github.com/xhongc/music-tag-web.git
synced 2026-02-02 17:59:07 +08:00
feature:支持保存专辑封面文件,支持自定义上传专辑封面
This commit is contained in:
@@ -28,6 +28,7 @@ class MusicId3Serializer(serializers.Serializer):
|
||||
year = serializers.CharField(required=True, allow_null=True, allow_blank=True)
|
||||
lyrics = serializers.CharField(required=True, allow_null=True, allow_blank=True)
|
||||
is_save_lyrics_file = serializers.BooleanField(required=True)
|
||||
is_save_album_cover = serializers.BooleanField(required=True)
|
||||
comment = serializers.CharField(required=True, allow_null=True, allow_blank=True)
|
||||
album_img = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
||||
filename = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
||||
@@ -85,3 +86,7 @@ class TaskSerializer(serializers.ModelSerializer):
|
||||
Task.objects.filter(id=ret["id"]).delete()
|
||||
ret["is_exists"] = False
|
||||
return ret
|
||||
|
||||
|
||||
class UploadImageSerializer(serializers.Serializer):
|
||||
upload_file = serializers.ImageField(required=True)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import base64
|
||||
import os
|
||||
|
||||
import music_tag
|
||||
@@ -83,7 +84,7 @@ def save_music(f, each, is_raw_thumbnail):
|
||||
if each.get("lyrics") is not None:
|
||||
f.remove_tag("lyrics")
|
||||
if each.get("is_save_lyrics_file", False):
|
||||
lyrics_file_path = f"{os.path.dirname(each['file_full_path'])}/{base_filename}.lrc"
|
||||
lyrics_file_path = f"{os.path.dirname(each['file_full_path'])}/cover-{base_filename}.lrc"
|
||||
if not os.path.exists(lyrics_file_path):
|
||||
with open(lyrics_file_path, "w", encoding="utf-8") as f_lyc2:
|
||||
f_lyc2.write(f["lyrics"].value)
|
||||
@@ -91,15 +92,37 @@ def save_music(f, each, is_raw_thumbnail):
|
||||
f["comment"] = each["comment"]
|
||||
if each.get("album_img", None):
|
||||
try:
|
||||
img_data = send().GET(each["album_img"])
|
||||
if img_data.status_code == 200:
|
||||
f['artwork'] = img_data.content
|
||||
if len(img_data.content) / 1024 / 1024 > 5:
|
||||
img_content = None
|
||||
if each["album_img"].startswith("http"):
|
||||
img_data = send().GET(each["album_img"])
|
||||
if img_data.status_code == 200:
|
||||
img_content = img_data.content
|
||||
else:
|
||||
img_content = base64.b64decode(each["album_img"])
|
||||
if img_content:
|
||||
f['artwork'] = img_content
|
||||
if each.get("is_save_album_cover", False):
|
||||
format_str = f['artwork'].value.format
|
||||
album_cover_path = f"{os.path.dirname(each['file_full_path'])}/cover-{f['album']}.{format_str}"
|
||||
if os.path.exists(album_cover_path):
|
||||
os.remove(album_cover_path)
|
||||
if not os.path.exists(album_cover_path):
|
||||
with open(album_cover_path, "wb") as f_img:
|
||||
f_img.write(img_content)
|
||||
if len(img_content) / 1024 / 1024 > 5:
|
||||
f['artwork'] = f['artwork'].first.raw_thumbnail([2048, 2048])
|
||||
if is_raw_thumbnail:
|
||||
f['artwork'] = f['artwork'].first.raw_thumbnail([2048, 2048])
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
else:
|
||||
if each.get("is_save_album_cover", False):
|
||||
format_str = f['artwork'].value.format
|
||||
album_cover_path = f"{os.path.dirname(each['file_full_path'])}/cover-{f['album']}.{format_str}"
|
||||
if not os.path.exists(album_cover_path):
|
||||
with open(album_cover_path, "wb") as f_img:
|
||||
f_img.write(f['artwork'].value.raw)
|
||||
if each.get("album_type", None):
|
||||
if isinstance(f.mfile.tags, VCFLACDict):
|
||||
f.mfile.tags["RELEASETYPE"] = each["album_type"]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import base64
|
||||
import copy
|
||||
import copy
|
||||
import os
|
||||
@@ -13,7 +14,7 @@ from applications.task.filters import TaskFilters
|
||||
from applications.task.models import TaskRecord, Task
|
||||
from applications.task.serialziers import FileListSerializer, Id3Serializer, UpdateId3Serializer, \
|
||||
FetchId3ByTitleSerializer, FetchLlyricSerializer, BatchUpdateId3Serializer, TranslationLycSerializer, \
|
||||
TidyFolderSerializer, TaskSerializer
|
||||
TidyFolderSerializer, TaskSerializer, UploadImageSerializer
|
||||
from applications.task.services.music_ids import MusicIDS
|
||||
from applications.task.services.music_resource import MusicResource
|
||||
from applications.task.services.update_ids import update_music_info
|
||||
@@ -42,6 +43,8 @@ class TaskViewSets(GenericViewSet):
|
||||
return TranslationLycSerializer
|
||||
elif self.action == "tidy_folder":
|
||||
return TidyFolderSerializer
|
||||
elif self.action == "upload_image":
|
||||
return UploadImageSerializer
|
||||
return FileListSerializer
|
||||
|
||||
@action(methods=['POST'], detail=False)
|
||||
@@ -288,6 +291,14 @@ class TaskViewSets(GenericViewSet):
|
||||
tidy_folder_task(music_id3_info, {"root_path": root_path, "first_dir": first_dir, "second_dir": second_dir})
|
||||
return self.success_response()
|
||||
|
||||
@action(methods=['POST'], detail=False)
|
||||
def upload_image(self, request, *args, **kwargs):
|
||||
upload_file = request.FILES.get('upload_file')
|
||||
|
||||
bs64_img = base64.b64encode(upload_file.read()).decode()
|
||||
# bs64_img_str = "data:image/jpeg;base64," + bs64_img
|
||||
return self.success_response(data=bs64_img)
|
||||
|
||||
@action(methods=["get"], detail=False)
|
||||
def clear_celery(self, request, *args, **kwargs):
|
||||
active_tasks = celery_app.control.inspect().active()
|
||||
|
||||
2
static/dist/index.prod.html
vendored
2
static/dist/index.prod.html
vendored
@@ -1,3 +1,3 @@
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>音乐标签Web版|Music Tag Web|</title><link rel="shortcut icon" href=/static/dist/img/favicon_64.ico type=image/x-icon><link href=./static/dist/css/app.css rel=stylesheet></head><body><script>window.siteUrl = "/"
|
||||
window.APP_CODE = 'dj-flow';
|
||||
window.CSRF_COOKIE_NAME = 'django_vue_cli_csrftoken'</script><div id=app></div><script type=text/javascript src=./static/dist/js/manifest.9ba6c0d4f4490e9a4f28.js></script><script type=text/javascript src=./static/dist/js/vendor.051dd49be048f27f51f9.js></script><script type=text/javascript src=./static/dist/js/app.44031ebb69b7019db6af.js></script></body></html>
|
||||
window.CSRF_COOKIE_NAME = 'django_vue_cli_csrftoken'</script><div id=app></div><script type=text/javascript src=./static/dist/js/manifest.9ba6c0d4f4490e9a4f28.js></script><script type=text/javascript src=./static/dist/js/vendor.051dd49be048f27f51f9.js></script><script type=text/javascript src=./static/dist/js/app.9dc8b5dd0c23d83774c4.js></script></body></html>
|
||||
1
static/dist/js/app.44031ebb69b7019db6af.js
vendored
1
static/dist/js/app.44031ebb69b7019db6af.js
vendored
File diff suppressed because one or more lines are too long
1
static/dist/js/app.9dc8b5dd0c23d83774c4.js
vendored
Normal file
1
static/dist/js/app.9dc8b5dd0c23d83774c4.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -173,16 +173,29 @@
|
||||
</div>
|
||||
<div class="edit-item" v-else-if="item === 'album_img'">
|
||||
<div class="label1">专辑封面:</div>
|
||||
<div style="width: 70%;display: flex;" v-if="reloadImg">
|
||||
<div v-if="musicInfo.album_img">
|
||||
<bk-image fit="contain" :src="musicInfo.album_img" style="width: 128px;"></bk-image>
|
||||
</div>
|
||||
<div v-else>
|
||||
<bk-image fit="contain" :src="musicInfo.artwork" style="width: 128px;"></bk-image>
|
||||
<div style="color: #63656e;font-size: 12px;">
|
||||
({{ musicInfo.artwork_w }}*{{ musicInfo.artwork_h }})
|
||||
{{ musicInfo.artwork_size }}MB
|
||||
<div style="display: flex;flex-direction: column;">
|
||||
<div style="width: 70%;display: flex;flex-direction: column;" v-if="reloadImg">
|
||||
<div>
|
||||
<bk-upload
|
||||
:files="files1"
|
||||
:theme="'picture'"
|
||||
:multiple="false"
|
||||
:with-credentials="true"
|
||||
:header="uploadHeader"
|
||||
:handle-res-code="handleRes"
|
||||
:size="{ maxFileSize: 5, maxImgSize: 5 }"
|
||||
:url="'http://127.0.0.1:8005/api/upload_image/'"
|
||||
name="upload_file"
|
||||
></bk-upload>
|
||||
</div>
|
||||
<div style="color: #63656e;font-size: 12px;display: flex;">
|
||||
<div>({{ musicInfo.artwork_w }}*{{ musicInfo.artwork_h }})</div>
|
||||
<div>{{ musicInfo.artwork_size }}MB</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex;margin-top: 10px;">
|
||||
<div class="label1">保存图片:</div>
|
||||
<bk-switcher v-model="musicInfo.is_save_album_cover"></bk-switcher>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -241,23 +254,26 @@
|
||||
<transition name="bk-slide-fade-left">
|
||||
<div style="margin-left: 40px;width: 500px;margin-top: 20px;" v-show="checkedIds.length > 0">
|
||||
<div style="width: 100%;display: flex;">
|
||||
<bk-button :theme="'success'" :loading="isLoading" @click="handleBatch" class="mr10"
|
||||
style="width: 50%;">
|
||||
<bk-button :theme="'primary'" :loading="isLoading" @click="handleBatch" class="mr10"
|
||||
style="width: 100%;">
|
||||
手动修改
|
||||
</bk-button>
|
||||
</div>
|
||||
<div style="width: 100%;display: flex;margin-top: 10px;">
|
||||
<bk-button :theme="'success'" :loading="isLoading"
|
||||
@click="exampleSetting1.primary.visible = true" class="mr10"
|
||||
style="width: 50%;">
|
||||
自动修改
|
||||
</bk-button>
|
||||
</div>
|
||||
<div style="width: 100%;display: flex;margin-top: 10px;">
|
||||
<bk-button :theme="'success'" :loading="isLoading"
|
||||
@click="exampleSetting2.primary.visible = true" class="mr10"
|
||||
style="width: 50%;">
|
||||
整理文件夹
|
||||
</bk-button>
|
||||
</div>
|
||||
<bk-divider>
|
||||
<div style="color: gray;font-size: 12px;">手动修改参数</div>
|
||||
</bk-divider>
|
||||
<div style="display: flex;margin-bottom: 10px;align-items: center;margin-top: 10px;">
|
||||
<div class="label1 can-copy" v-bk-tooltips="'变量名:${title}'" v-bk-copy="'${title}'">标题:</div>
|
||||
<div style="width: 70%;">
|
||||
@@ -365,14 +381,25 @@
|
||||
</div>
|
||||
<div class="edit-item" v-else-if="item === 'album_img'">
|
||||
<div class="label1">专辑封面:</div>
|
||||
<div style="width: 70%;">
|
||||
<div v-if="musicInfoManual.album_img">
|
||||
<bk-image fit="contain" :src="musicInfoManual.album_img" style="width: 128px;"
|
||||
v-if="reloadImg"></bk-image>
|
||||
<div style="display: flex;flex-direction: column;">
|
||||
<div style="width: 70%;display: flex;flex-direction: column;" v-if="reloadImg">
|
||||
<div>
|
||||
<bk-upload
|
||||
:files="files1"
|
||||
:theme="'picture'"
|
||||
:multiple="false"
|
||||
:with-credentials="true"
|
||||
:header="uploadHeader"
|
||||
:handle-res-code="handleResBatch"
|
||||
:size="{ maxFileSize: 5, maxImgSize: 5 }"
|
||||
:url="'http://127.0.0.1:8005/api/upload_image/'"
|
||||
name="upload_file"
|
||||
></bk-upload>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="musicInfoManual.artwork">
|
||||
<bk-image fit="contain" :src="musicInfoManual.artwork" style="width: 128px;"
|
||||
v-if="!musicInfoManual.album_img"></bk-image>
|
||||
<div style="display: flex;margin-top: 10px;">
|
||||
<div class="label1">保存图片:</div>
|
||||
<bk-switcher v-model="musicInfoManual.is_save_album_cover"></bk-switcher>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -580,6 +607,11 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
files1: [],
|
||||
uploadHeader: [
|
||||
{name: 'X-CSRFToken', value: this.getCookie('django_vue_cli_csrftoken')},
|
||||
{name: 'AUTHORIZATION', value: this.getCookie('AUTHORIZATION')}
|
||||
],
|
||||
showFields: localStorage.getItem('showFields') ? JSON.parse(localStorage.getItem('showFields')) : ['filename', 'artist', 'album', 'albumartist', 'genre', 'year', 'lyrics', 'comment', 'album_img'],
|
||||
fieldList: [
|
||||
{id: 'filename', name: '文件名'},
|
||||
@@ -641,15 +673,18 @@
|
||||
],
|
||||
baseMusicInfo: {
|
||||
'genre': '流行',
|
||||
'is_save_lyrics_file': false
|
||||
'is_save_lyrics_file': false,
|
||||
'is_save_album_cover': false
|
||||
},
|
||||
musicInfo: {
|
||||
'genre': '流行',
|
||||
'is_save_lyrics_file': false
|
||||
'is_save_lyrics_file': false,
|
||||
'is_save_album_cover': false
|
||||
},
|
||||
musicInfoManual: {
|
||||
'genre': '流行',
|
||||
'is_save_lyrics_file': false
|
||||
'is_save_lyrics_file': false,
|
||||
'is_save_album_cover': false
|
||||
},
|
||||
fadeShowDir: false,
|
||||
fadeShowDetail: false,
|
||||
@@ -739,7 +774,6 @@
|
||||
// 如果在某些情况下 h 不能自动注入而报错,需将 h 参数写上;一般来说 h 默认是第一参数,但是现在改为第一参数会导致已经使用的用户都需要修改,所以先放在最后。
|
||||
// 如果 h 能自动注入则可以忽略 h 参数,无需写上,否则 h 参数会重复。
|
||||
const titleClass = node.selected ? 'node-title node-selected' : 'node-title ' + node.state
|
||||
console.log(node, titleClass)
|
||||
if (node.title.length > 30) {
|
||||
return <span>
|
||||
<span class={titleClass} domPropsInnerHTML={node.title.slice(0, 30)}
|
||||
@@ -795,6 +829,14 @@
|
||||
if (res.result) {
|
||||
this.musicInfo = res.data
|
||||
this.musicInfo.is_save_lyrics_file = false
|
||||
this.musicInfo.is_save_album_cover = false
|
||||
this.files1 = [
|
||||
{
|
||||
name: 'cover.png',
|
||||
status: 'done',
|
||||
url: this.musicInfo.artwork
|
||||
}
|
||||
]
|
||||
} else {
|
||||
this.$cwMessage(res.message, 'error')
|
||||
}
|
||||
@@ -855,12 +897,18 @@
|
||||
})
|
||||
} else if (k === 'album_img') {
|
||||
this.musicInfo[k] = v
|
||||
this.files1 = [
|
||||
{
|
||||
name: 'cover.png',
|
||||
status: 'done',
|
||||
url: v
|
||||
}
|
||||
]
|
||||
this.reloadImg = false
|
||||
this.$nextTick(() => {
|
||||
this.reloadImg = true
|
||||
})
|
||||
} else if (k === 'lyric_tran') {
|
||||
console.log(v)
|
||||
this.musicInfo['lyrics'] = v
|
||||
} else {
|
||||
this.musicInfo[k] = v
|
||||
@@ -917,6 +965,8 @@
|
||||
if (res.result) {
|
||||
this.treeListOne = res.data
|
||||
this.fadeShowDir = true
|
||||
} else {
|
||||
this.$cwMessage(res.message, 'error')
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -1041,6 +1091,22 @@
|
||||
const obj = JSON.stringify(this.showFields)
|
||||
window.localStorage.setItem('showFields', obj)
|
||||
window.localStorage.setItem('resource', this.resource)
|
||||
},
|
||||
handleRes(response) {
|
||||
if (response.result) {
|
||||
this.musicInfo.album_img = response.data
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
handleResBatch(response) {
|
||||
if (response.result) {
|
||||
this.musicInfoManual.album_img = response.data
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user