diff --git a/api/store/model/suno_job.go b/api/store/model/suno_job.go new file mode 100644 index 00000000..7add83e7 --- /dev/null +++ b/api/store/model/suno_job.go @@ -0,0 +1,32 @@ +package model + +import "time" + +type SunoJob struct { + Id uint `gorm:"primarykey;column:id"` + UserId int + Title string + Type string + TaskId string + ReferenceId string // 续写的任务id + Tags string // 歌曲风格和标签 + Instrumental bool // 是否生成纯音乐 + ExtendSecs int // 续写秒数 + SongId int // 续写的歌曲id + Prompt string // 提示词 + ThumbImgURL string // 缩略图 URL + CoverImgURL string // 封面图 URL + AudioURL string // 音频 URL + ModelName string // 模型名称 + Progress int // 任务进度 + Duration int // 银屏时长,秒 + Publish bool // 是否发布 + ErrMsg string // 错误信息 + RawData string // 原始数据 json + Power int // 消耗算力 + CreatedAt time.Time +} + +func (SunoJob) TableName() string { + return "chatgpt_suno_jobs" +} diff --git a/api/store/vo/suno_job.go b/api/store/vo/suno_job.go new file mode 100644 index 00000000..cb76fc13 --- /dev/null +++ b/api/store/vo/suno_job.go @@ -0,0 +1,32 @@ +package vo + +import "time" + +type SunoJob struct { + Id uint `json:"id"` + UserId int `json:"user_id"` + Title string `json:"title"` + Type string `json:"type"` + TaskId string `json:"task_id"` + ReferenceId string `json:"reference_id"` // 续写的任务id + Tags string `json:"tags"` // 歌曲风格和标签 + Instrumental bool `json:"instrumental"` // 是否生成纯音乐 + ExtendSecs int `json:"extend_secs"` // 续写秒数 + SongId int `json:"song_id"` // 续写的歌曲id + Prompt string `json:"prompt"` // 提示词 + ThumbImgURL string `json:"thumb_img_url"` // 缩略图 URL + CoverImgURL string `json:"cover_img_url"` // 封面图 URL + AudioURL string `json:"audio_url"` // 音频 URL + ModelName string `json:"model_name"` // 模型名称 + Progress int `json:"progress"` // 任务进度 + Duration int `json:"duration"` // 银屏时长,秒 + Publish bool `json:"publish"` // 是否发布 + ErrMsg string `json:"err_msg"` // 错误信息 + RawData string `json:"raw_data"` // 原始数据 json + Power int `json:"power"` // 消耗算力 + CreatedAt time.Time +} + +func (SunoJob) TableName() string { + return "chatgpt_suno_jobs" +} diff --git a/database/update-v4.1.1.sql b/database/update-v4.1.1.sql new file mode 100644 index 00000000..e9ff28cb --- /dev/null +++ b/database/update-v4.1.1.sql @@ -0,0 +1,35 @@ +CREATE TABLE `chatgpt_suno_jobs` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户 ID', + `title` varchar(100) DEFAULT NULL COMMENT '歌曲标题', + `type` tinyint(1) DEFAULT '0' COMMENT '任务类型,1:灵感创作,2:自定义创作', + `task_id` varchar(50) DEFAULT NULL COMMENT '任务 ID', + `reference_id` char(50) DEFAULT NULL COMMENT '引用任务 ID', + `tags` varchar(100) DEFAULT NULL COMMENT '歌曲风格', + `instrumental` tinyint(1) DEFAULT '0' COMMENT '是否为纯音乐', + `extend_secs` smallint DEFAULT '0' COMMENT '延长秒数', + `song_id` varchar(50) DEFAULT NULL COMMENT '要续写的歌曲 ID', + `prompt` varchar(2000) NOT NULL COMMENT '提示词', + `thumb_img_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '缩略图地址', + `cover_img_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '封面图地址', + `audio_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '音频地址', + `model_name` varchar(30) DEFAULT NULL COMMENT '模型地址', + `progress` smallint DEFAULT '0' COMMENT '任务进度', + `duration` smallint NOT NULL DEFAULT '0' COMMENT '歌曲时长', + `publish` tinyint(1) NOT NULL COMMENT '是否发布', + `err_msg` varchar(255) DEFAULT NULL COMMENT '错误信息', + `raw_data` text COMMENT '原始数据', + `power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='MidJourney 任务表'; + +-- +-- 转储表的索引 +-- + +-- +-- 表的索引 `chatgpt_suno_jobs` +-- +ALTER TABLE `chatgpt_suno_jobs` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `task_id` (`task_id`); diff --git a/web/public/files/suno.mp3 b/web/public/files/suno.mp3 new file mode 100644 index 00000000..4aa2a844 Binary files /dev/null and b/web/public/files/suno.mp3 differ diff --git a/web/public/images/create-new.svg b/web/public/images/create-new.svg new file mode 100644 index 00000000..335cf9a6 --- /dev/null +++ b/web/public/images/create-new.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web/public/images/menu/music.png b/web/public/images/menu/music.png new file mode 100644 index 00000000..c699e828 Binary files /dev/null and b/web/public/images/menu/music.png differ diff --git a/web/public/images/menu/suno.png b/web/public/images/menu/suno.png new file mode 100644 index 00000000..ecd4b9cd Binary files /dev/null and b/web/public/images/menu/suno.png differ diff --git a/web/src/assets/css/suno.styl b/web/src/assets/css/suno.styl new file mode 100644 index 00000000..de27936a --- /dev/null +++ b/web/src/assets/css/suno.styl @@ -0,0 +1,180 @@ +.page-suno { + display flex + height 100% + background-color #0E0808 + overflow auto + + .left-bar { + max-width 340px + min-width 340px + padding 20px 30px + + .bar-top { + display flex + flex-flow row + justify-content: space-between; + } + + .params { + padding 20px 0 + color rgb(250 247 245) + position relative + + .pure-music { + position absolute + right 0 + top 24px + display flex + + .text { + margin-top 5px + margin-left 5px + } + } + + .label { + padding 10px 0 + + .text { + margin-right 10px + } + } + .item { + margin-bottom: 20px + + .create-btn { + margin 20px 0 + background-image url("~@/assets/img/suno-create-bg.svg") + background-size: cover; + background-position: 50% 50%; + transition: background 1s ease-in-out; + overflow: hidden; + font-size 16px + width 100% + padding 16px + border-radius 25px + border none + cursor pointer + + img { + position relative + top 3px + margin-right 5px + } + + &:hover { + opacity: 0.9; + } + } + } + } + } + .right-box { + width 100% + color rgb(250 247 245) + + .list-box { + padding 0 0 0 20px + .item { + display flex + flex-flow row + padding 5px 0 + cursor pointer + + &:hover { + background-color #1C1616 + } + + .left { + .container { + width 60px + height 90px + position relative + + .el-image { + height 90px + border-radius 5px + } + + .duration { + position absolute + bottom 0 + right 0 + background-color rgba(14,8,8,.7) + padding 0 2px + font-family 'Input Sans' + font-size 14px + font-weight 700 + border-radius .125rem + } + } + } + + .center { + width 100% + //border 1px solid saddlebrown + display flex + justify-content center + align-items flex-start + flex-flow column + height 90px + padding 0 20px + + .title { + padding 6px 0 + font-size 16px + font-weight 700 + + .model { + color #E2E8F0 + background-color #1C1616 + border 1px solid #8f8f8f + font-weight normal + font-size 14px + padding 1px 3px + border-radius 5px + margin-left 10px + } + } + + .tags { + font-size 14px + color #d1d1d1 + padding 3px 0 + } + } + + .right { + width 200px; + font-size 14px + padding 0 15px + + .tools { + display flex + justify-content left + align-items center + flex-flow row + height 90px + + .btn { + background-color #363030 + border none + border-radius 5px + padding 5px 10px + cursor pointer + + &:hover { + background-color #5F5958 + } + } + } + } + } + + .item.active { + background-color #2A2525 + } + } + } + +} \ No newline at end of file diff --git a/web/src/assets/img/suno-create-bg.svg b/web/src/assets/img/suno-create-bg.svg new file mode 100644 index 00000000..8ac78e05 --- /dev/null +++ b/web/src/assets/img/suno-create-bg.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/src/components/admin/AdminSidebar.vue b/web/src/components/admin/AdminSidebar.vue index 4c0aebda..9238b58e 100644 --- a/web/src/components/admin/AdminSidebar.vue +++ b/web/src/components/admin/AdminSidebar.vue @@ -58,8 +58,8 @@ import {httpGet} from "@/utils/http"; import {ElMessage} from "element-plus"; import {useRoute} from "vue-router"; -const title = ref('Chat-Plus-Admin') -const logo = ref('/images/logo.png') +const title = ref('') +const logo = ref('') // 加载系统配置 httpGet('/api/admin/config/get?key=system').then(res => { diff --git a/web/src/components/ui/BlackInput.vue b/web/src/components/ui/BlackInput.vue new file mode 100644 index 00000000..ef06bd1a --- /dev/null +++ b/web/src/components/ui/BlackInput.vue @@ -0,0 +1,53 @@ + + + + + + + + + + \ No newline at end of file diff --git a/web/src/components/ui/BlackSelect.vue b/web/src/components/ui/BlackSelect.vue new file mode 100644 index 00000000..0359b1ba --- /dev/null +++ b/web/src/components/ui/BlackSelect.vue @@ -0,0 +1,43 @@ + + + + + + + + + + diff --git a/web/src/components/ui/BlackSwitch.vue b/web/src/components/ui/BlackSwitch.vue new file mode 100644 index 00000000..c1376f14 --- /dev/null +++ b/web/src/components/ui/BlackSwitch.vue @@ -0,0 +1,25 @@ + + + + + + + diff --git a/web/src/router.js b/web/src/router.js index 1b826350..0dfe3798 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -82,6 +82,12 @@ const routes = [ meta: {title: 'DALLE-3'}, component: () => import('@/views/Dalle.vue'), }, + { + name: 'suno', + path: '/suno', + meta: {title: 'Suno音乐创作'}, + component: () => import('@/views/Suno.vue'), + }, { name: 'ExternalLink', path: '/external', diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue index 50e299a6..d348793b 100644 --- a/web/src/views/Home.vue +++ b/web/src/views/Home.vue @@ -146,7 +146,7 @@ import ConfigDialog from "@/components/UserInfoDialog.vue"; import {showMessageError} from "@/utils/dialog"; const router = useRouter(); -const logo = ref('/images/logo.png'); +const logo = ref(''); const mainNavs = ref([]) const moreNavs = ref([]) const curPath = ref(router.currentRoute.value.path) diff --git a/web/src/views/Index.vue b/web/src/views/Index.vue index ecccd03c..fc805497 100644 --- a/web/src/views/Index.vue +++ b/web/src/views/Index.vue @@ -77,9 +77,9 @@ if (isMobile()) { router.push("/mobile") } -const title = ref("Geek-AI 创作系统") -const logo = ref("/images/logo.png") -const slogan = ref("我辈之人,先干为敬,陪您先把 AI 用起来") +const title = ref("") +const logo = ref("") +const slogan = ref("") const license = ref({}) const winHeight = window.innerHeight - 150 const bgImgUrl = ref('') diff --git a/web/src/views/Login.vue b/web/src/views/Login.vue index 74703858..bb55eedd 100644 --- a/web/src/views/Login.vue +++ b/web/src/views/Login.vue @@ -79,7 +79,7 @@ const title = ref('Geek-AI'); const username = ref(process.env.VUE_APP_USER); const password = ref(process.env.VUE_APP_PASS); const showResetPass = ref(false) -const logo = ref("/images/logo.png") +const logo = ref("") const licenseConfig = ref({}) const wechatLoginURL = ref('') diff --git a/web/src/views/Register.vue b/web/src/views/Register.vue index 71b741e0..f526acc5 100644 --- a/web/src/views/Register.vue +++ b/web/src/views/Register.vue @@ -183,8 +183,8 @@ import {validateEmail, validateMobile} from "@/utils/validate"; import {showMessageError, showMessageOK} from "@/utils/dialog"; const router = useRouter(); -const title = ref('Geek-AI 用户注册'); -const logo = ref("/images/logo") +const title = ref(''); +const logo = ref("") const data = ref({ username: '', password: '', diff --git a/web/src/views/Suno.vue b/web/src/views/Suno.vue new file mode 100644 index 00000000..9b106ef4 --- /dev/null +++ b/web/src/views/Suno.vue @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + 纯音乐 + + + + + 歌词 + + + + + + + + + + + + + + + + 音乐风格 + + + + + + + + + + + + + + + + 歌曲名称 + + + + + + + + + + + + + + + + + 歌曲描述 + + + + + + + + + + + + + + + + + + 生成音乐 + + + + + + + + + + + {{duration(item.duration)}} + + + + + {{item.title}} + {{item.model}} + + {{item.tags}} + + + + + 续写 + + + + + + + + + + + + + diff --git a/web/src/views/admin/Login.vue b/web/src/views/admin/Login.vue index d1a59f0c..9d4dfcdd 100644 --- a/web/src/views/admin/Login.vue +++ b/web/src/views/admin/Login.vue @@ -59,7 +59,7 @@ const router = useRouter(); const title = ref('Geek-AI Console'); const username = ref(process.env.VUE_APP_ADMIN_USER); const password = ref(process.env.VUE_APP_ADMIN_PASS); -const logo = ref("/images/logo.png") +const logo = ref("") checkAdminSession().then(() => { router.push("/admin")