Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
live-management-web
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
haojie
live-management-web
Commits
6f2f1890
Commit
6f2f1890
authored
Aug 10, 2023
by
haojie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
创建直播音频切割
parent
9dd61e5a
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
138 additions
and
35 deletions
+138
-35
src/components/MultipleUpload/index.tsx
+3
-2
src/hooks/useScript.ts
+36
-3
src/layouts/components/Content.vue
+0
-1
src/pages/createLive/components/scripts.vue
+14
-14
src/pages/createLive/index.vue
+36
-11
src/service/CreateLive.ts
+16
-0
src/store/modules/navbar.ts
+0
-1
src/utils/tool.ts
+33
-3
No files found.
src/components/MultipleUpload/index.tsx
View file @
6f2f1890
...
@@ -64,7 +64,8 @@ export default defineComponent({
...
@@ -64,7 +64,8 @@ export default defineComponent({
}
else
{
}
else
{
Curfile
.
status
=
0
;
Curfile
.
status
=
0
;
}
}
emit
(
'update:modelValue'
,
list
);
console
.
log
(
fileList
.
value
);
emit
(
'update:modelValue'
,
fileList
.
value
);
// 再提交一份原始list
// 再提交一份原始list
if
(
change
)
{
if
(
change
)
{
emit
(
'change'
,
list
,
fileList
.
value
);
emit
(
'change'
,
list
,
fileList
.
value
);
...
@@ -330,7 +331,7 @@ export default defineComponent({
...
@@ -330,7 +331,7 @@ export default defineComponent({
</
span
>
</
span
>
</
div
>
</
div
>
)
}
)
}
<
div
class=
"file-name"
>
{
item
.
file
?.
name
}
</
div
>
<
div
class=
"file-name"
>
{
item
.
name
?
item
.
name
:
item
.
file
?.
name
}
</
div
>
{
computedTotalTime
()
?
(
{
computedTotalTime
()
?
(
<
audio
<
audio
src=
{
item
.
url
}
src=
{
item
.
url
}
...
...
src/hooks/useScript.ts
View file @
6f2f1890
...
@@ -70,7 +70,7 @@ export const processTextCallback = () => {
...
@@ -70,7 +70,7 @@ export const processTextCallback = () => {
);
);
// 提交时过滤必要的字段
// 提交时过滤必要的字段
const
filterFiled
=
()
=>
{
const
filterFiled
=
(
type
:
string
=
''
)
=>
{
let
item
=
createLiveInfo
.
value
;
let
item
=
createLiveInfo
.
value
;
// 过滤必须字段
// 过滤必须字段
let
params
:
any
=
{};
let
params
:
any
=
{};
...
@@ -106,6 +106,14 @@ export const processTextCallback = () => {
...
@@ -106,6 +106,14 @@ export const processTextCallback = () => {
};
};
});
});
}
else
{
}
else
{
// 切割后的音频格式
const
audioConversion
=
(
list
:
any
[])
=>
{
return
list
.
map
((
child
:
any
)
=>
{
return
child
.
audio_url
;
})
.
join
(
'|'
);
};
// 音频
// 音频
params
.
type_content
=
item
[
createLiveKeys
.
audioScriptList
].
map
((
audioScript
:
any
)
=>
{
params
.
type_content
=
item
[
createLiveKeys
.
audioScriptList
].
map
((
audioScript
:
any
)
=>
{
let
list
=
[];
let
list
=
[];
...
@@ -114,7 +122,31 @@ export const processTextCallback = () => {
...
@@ -114,7 +122,31 @@ export const processTextCallback = () => {
content
:
''
,
content
:
''
,
old_content
:
''
,
old_content
:
''
,
name
:
''
,
name
:
''
,
uuid
:
v4
(),
};
};
// 草稿类型的
if
(
type
==
'edit_drafts'
)
{
if
(
it
.
children
&&
it
.
children
.
length
>
1
)
{
// 有切割的
params
.
content
=
audioConversion
(
it
.
children
);
}
if
(
it
.
old_content
)
{
params
.
old_content
=
it
.
old_content
;
}
else
if
(
it
.
url
)
{
params
.
old_content
=
it
.
url
;
}
if
(
it
.
content
)
{
params
.
content
=
it
.
content
;
}
else
if
(
it
.
url
)
{
params
.
content
=
it
.
url
;
}
}
else
{
// 获取编辑状态下的content
if
(
it
.
children
&&
it
.
children
.
length
>
1
)
{
// 修改content
params
.
content
=
audioConversion
(
it
.
children
);
params
.
old_content
=
it
.
audio_url
;
}
else
{
if
(
typeof
it
===
'string'
)
{
if
(
typeof
it
===
'string'
)
{
params
.
content
=
it
;
params
.
content
=
it
;
params
.
old_content
=
it
;
params
.
old_content
=
it
;
...
@@ -125,10 +157,11 @@ export const processTextCallback = () => {
...
@@ -125,10 +157,11 @@ export const processTextCallback = () => {
params
.
content
=
it
.
audio_url
;
params
.
content
=
it
.
audio_url
;
params
.
old_content
=
it
.
audio_url
;
params
.
old_content
=
it
.
audio_url
;
}
}
}
}
// 切割后的
if
(
typeof
it
!==
'string'
&&
it
.
new_content
)
{
if
(
typeof
it
!==
'string'
&&
it
.
new_content
)
{
params
.
content
=
it
.
new_content
;
params
.
content
=
it
.
new_content
;
params
.
uuid
=
it
.
uuid
;
}
}
// 获取文件名
// 获取文件名
...
...
src/layouts/components/Content.vue
View file @
6f2f1890
...
@@ -31,7 +31,6 @@ watch(
...
@@ -31,7 +31,6 @@ watch(
()
=>
keepAliveList
.
value
,
()
=>
keepAliveList
.
value
,
(
v
)
=>
{
(
v
)
=>
{
currentCacheList
.
value
=
getCacheList
();
currentCacheList
.
value
=
getCacheList
();
console
.
log
(
currentCacheList
.
value
);
},
},
{
{
deep
:
true
,
deep
:
true
,
...
...
src/pages/createLive/components/scripts.vue
View file @
6f2f1890
...
@@ -183,7 +183,7 @@
...
@@ -183,7 +183,7 @@
</template>
</template>
<
script
lang=
"tsx"
setup
>
<
script
lang=
"tsx"
setup
>
import
{
computed
,
onMounted
,
reactive
,
ref
,
watch
}
from
'vue'
;
import
{
computed
,
onMounted
,
reactive
,
ref
,
watch
,
toRaw
}
from
'vue'
;
import
Button
from
'@/components/Button.vue'
;
import
Button
from
'@/components/Button.vue'
;
import
MultipleUpload
from
'@/components/MultipleUpload'
;
import
MultipleUpload
from
'@/components/MultipleUpload'
;
import
CheckBox
from
'@/components/CheckBox.vue'
;
import
CheckBox
from
'@/components/CheckBox.vue'
;
...
@@ -193,7 +193,13 @@ import ScriptTemplate from '@/components/ScriptTemplate.vue';
...
@@ -193,7 +193,13 @@ import ScriptTemplate from '@/components/ScriptTemplate.vue';
import
Select
from
'@/components/Select.vue'
;
import
Select
from
'@/components/Select.vue'
;
import
SelectionPopup
from
'@/components/SelectionPopup.vue'
;
import
SelectionPopup
from
'@/components/SelectionPopup.vue'
;
import
{
show_message
,
isDev
,
ecursionDeepCopy
}
from
'@/utils/tool'
;
import
{
show_message
,
isDev
,
ecursionDeepCopy
}
from
'@/utils/tool'
;
import
{
createLiveKeys
,
scriptTypeList
,
scriptTypeText
,
scriptTypePhonetics
}
from
'@/service/CreateLive'
;
import
{
createLiveKeys
,
scriptTypeList
,
scriptTypeText
,
scriptTypePhonetics
,
mergeSameAudio
,
}
from
'@/service/CreateLive'
;
import
{
useLiveInfoSubmit
}
from
'@/hooks/useStoreCommit'
;
import
{
useLiveInfoSubmit
}
from
'@/hooks/useStoreCommit'
;
import
{
getUploadConfig
,
getTonesList
}
from
'@/service/Common'
;
import
{
getUploadConfig
,
getTonesList
}
from
'@/service/Common'
;
import
{
useStore
}
from
'vuex'
;
import
{
useStore
}
from
'vuex'
;
...
@@ -296,7 +302,7 @@ const createUploadFile = (list: any[], oldList: any[]) => {
...
@@ -296,7 +302,7 @@ const createUploadFile = (list: any[], oldList: any[]) => {
};
};
// 音频脚本编辑后
// 音频脚本编辑后
const
uploadEdit
=
(
url
:
string
,
id
:
number
)
=>
{
const
uploadEdit
=
(
list
:
any
[],
oldList
:
any
[]
)
=>
{
uploadChange
();
uploadChange
();
};
};
...
@@ -420,7 +426,6 @@ const updateInfo = (info: any) => {
...
@@ -420,7 +426,6 @@ const updateInfo = (info: any) => {
}
else
if
(
info
.
content
)
{
}
else
if
(
info
.
content
)
{
// 草稿
// 草稿
type
=
info
.
content
.
type
;
type
=
info
.
content
.
type
;
console
.
log
(
type
);
if
(
type
==
scriptTypeText
)
{
if
(
type
==
scriptTypeText
)
{
// 文本
// 文本
type_content
=
info
.
content
.
content
;
type_content
=
info
.
content
.
content
;
...
@@ -447,16 +452,8 @@ const updateInfo = (info: any) => {
...
@@ -447,16 +452,8 @@ const updateInfo = (info: any) => {
currentOption
.
value
=
scriptTypePhonetics
;
currentOption
.
value
=
scriptTypePhonetics
;
if
(
route
.
query
.
type
==
'edit'
)
{
if
(
route
.
query
.
type
==
'edit'
)
{
if
(
type_content
)
{
if
(
type_content
)
{
audioScriptList
.
value
=
type_content
.
map
((
item
:
any
)
=>
{
// 合并同类
let
list
=
[];
audioScriptList
.
value
=
mergeSameAudio
(
ecursionDeepCopy
(
toRaw
(
type_content
)));
item
.
forEach
((
it
:
any
)
=>
{
// 找到所有uuid一致的
// it.uuid
});
return
{
data
:
item
,
};
});
}
else
{
}
else
{
audioScriptList
.
value
=
[];
audioScriptList
.
value
=
[];
}
}
...
@@ -805,6 +802,9 @@ onMounted(async () => {
...
@@ -805,6 +802,9 @@ onMounted(async () => {
.custom-multiple-upload
{
.custom-multiple-upload
{
height
:
100%
;
height
:
100%
;
background
:
transparent
;
background
:
transparent
;
.custom-uploading-stauts
{
margin-top
:
12px
;
}
}
}
}
}
}
}
...
...
src/pages/createLive/index.vue
View file @
6f2f1890
...
@@ -73,9 +73,17 @@ import HomeSvg from '@/assets/svg/createLive/home.svg';
...
@@ -73,9 +73,17 @@ import HomeSvg from '@/assets/svg/createLive/home.svg';
import
InteractSvg
from
'@/assets/svg/createLive/interact.svg'
;
import
InteractSvg
from
'@/assets/svg/createLive/interact.svg'
;
import
ScriptsSvg
from
'@/assets/svg/createLive/scripts.svg'
;
import
ScriptsSvg
from
'@/assets/svg/createLive/scripts.svg'
;
import
{
computed
,
onBeforeMount
,
ref
,
onBeforeUnmount
,
onActivated
}
from
'vue'
;
import
{
computed
,
onBeforeMount
,
ref
,
onBeforeUnmount
,
onActivated
}
from
'vue'
;
import
{
getElBounding
,
show_message
,
isDev
,
DataType
,
dimensionalConvert
,
getDurationOfAudioFile
}
from
'@/utils/tool'
;
import
{
getElBounding
,
show_message
,
isDev
,
DataType
,
dimensionalConvert
,
ecursionDeepCopy
,
getDurationOfAudioFile
,
}
from
'@/utils/tool'
;
import
{
useStore
}
from
'vuex'
;
import
{
useStore
}
from
'vuex'
;
import
{
createLiveKeys
,
scriptTypeText
,
scriptTypePhonetics
}
from
'@/service/CreateLive'
;
import
{
createLiveKeys
,
scriptTypeText
,
scriptTypePhonetics
,
mergeSameAudio
}
from
'@/service/CreateLive'
;
import
{
getLiveTaskInfo
,
createDrafts
,
getDraftsDetail
,
liveTts
,
createLiveTask
}
from
'@/utils/api/userApi'
;
import
{
getLiveTaskInfo
,
createDrafts
,
getDraftsDetail
,
liveTts
,
createLiveTask
}
from
'@/utils/api/userApi'
;
import
{
useRoute
,
onBeforeRouteLeave
}
from
'vue-router'
;
import
{
useRoute
,
onBeforeRouteLeave
}
from
'vue-router'
;
import
routerConfig
from
'@/router/tool'
;
import
routerConfig
from
'@/router/tool'
;
...
@@ -211,11 +219,7 @@ const getEditInfo = async (id: any, type: string) => {
...
@@ -211,11 +219,7 @@ const getEditInfo = async (id: any, type: string) => {
// 音频音色
// 音频音色
params
[
createLiveKeys
.
phoneticsSoundColor
]
=
res
.
data
.
phonetic_timbres_id
;
params
[
createLiveKeys
.
phoneticsSoundColor
]
=
res
.
data
.
phonetic_timbres_id
;
params
[
createLiveKeys
.
phoneticsFile
]
=
res
.
data
.
type_content
;
params
[
createLiveKeys
.
phoneticsFile
]
=
res
.
data
.
type_content
;
params
[
createLiveKeys
.
audioScriptList
]
=
res
.
data
.
type_content
.
map
((
item
:
any
)
=>
{
params
[
createLiveKeys
.
audioScriptList
]
=
mergeSameAudio
(
res
.
data
.
type_content
);
return
{
data
:
item
,
};
});
}
}
// 更新标题
// 更新标题
if
(
res
.
data
.
name
)
{
if
(
res
.
data
.
name
)
{
...
@@ -246,11 +250,24 @@ const getEditInfo = async (id: any, type: string) => {
...
@@ -246,11 +250,24 @@ const getEditInfo = async (id: any, type: string) => {
// 音频音色
// 音频音色
params
[
createLiveKeys
.
phoneticsSoundColor
]
=
content
.
phonetic_timbres_id
?
content
.
phonetic_timbres_id
:
''
;
params
[
createLiveKeys
.
phoneticsSoundColor
]
=
content
.
phonetic_timbres_id
?
content
.
phonetic_timbres_id
:
''
;
if
(
content
.
type_content
)
{
if
(
content
.
type_content
)
{
params
[
createLiveKeys
.
audioScriptList
]
=
content
.
type_content
.
map
((
item
:
any
)
=>
{
let
newContent
=
ecursionDeepCopy
(
content
.
type_content
);
// 过滤出child
newContent
=
newContent
.
map
((
item
:
any
)
=>
{
item
.
forEach
((
it
:
any
)
=>
{
it
.
children
=
[];
let
children
=
it
.
content
.
split
(
'|'
);
children
.
forEach
((
child
)
=>
{
let
obj
=
{
audio_url
:
child
,
};
it
.
children
.
push
(
obj
);
});
});
return
{
return
{
data
:
item
,
data
:
item
,
};
};
});
});
params
[
createLiveKeys
.
audioScriptList
]
=
newContent
;
}
}
}
}
// 更新标题
// 更新标题
...
@@ -363,8 +380,9 @@ const onSaveDrafts = async () => {
...
@@ -363,8 +380,9 @@ const onSaveDrafts = async () => {
// 编辑时音频提交
// 编辑时音频提交
const
editAudioSave
=
async
()
=>
{
const
editAudioSave
=
async
()
=>
{
try
{
try
{
let
params
=
filterFiled
();
loading
.
value
=
true
;
loading
.
value
=
true
;
await
audioSplit
();
let
params
=
filterFiled
();
let
res
:
any
=
await
onUpdateLiveTask
(
route
.
query
.
id
,
params
);
let
res
:
any
=
await
onUpdateLiveTask
(
route
.
query
.
id
,
params
);
if
(
res
)
{
if
(
res
)
{
if
(
route
.
query
.
type
===
'edit'
)
{
if
(
route
.
query
.
type
===
'edit'
)
{
...
@@ -439,6 +457,10 @@ const audioSplit = async () => {
...
@@ -439,6 +457,10 @@ const audioSplit = async () => {
let
item
=
createLiveInfo
.
value
[
createLiveKeys
.
audioScriptList
][
i
];
let
item
=
createLiveInfo
.
value
[
createLiveKeys
.
audioScriptList
][
i
];
for
(
let
j
=
0
;
j
<
item
.
data
.
length
;
j
++
)
{
for
(
let
j
=
0
;
j
<
item
.
data
.
length
;
j
++
)
{
let
row
=
item
.
data
[
j
];
let
row
=
item
.
data
[
j
];
if
(
row
.
children
&&
row
.
children
.
length
>
1
)
{
// 编辑时没有修改参数
continue
;
}
if
(
row
.
file
&&
row
.
file
.
raw
)
{
if
(
row
.
file
&&
row
.
file
.
raw
)
{
// 文件时长
// 文件时长
let
fileDuration
=
await
getDurationOfAudioFile
(
row
.
file
.
raw
);
let
fileDuration
=
await
getDurationOfAudioFile
(
row
.
file
.
raw
);
...
@@ -458,7 +480,6 @@ const audioSplit = async () => {
...
@@ -458,7 +480,6 @@ const audioSplit = async () => {
// console.log(list, 'list');
// console.log(list, 'list');
// 一维数组
// 一维数组
row
.
new_content
=
list
.
join
(
'|'
);
row
.
new_content
=
list
.
join
(
'|'
);
row
.
uuid
=
v4
();
}
}
}
}
}
}
...
@@ -471,7 +492,11 @@ const audioSubmit = async () => {
...
@@ -471,7 +492,11 @@ const audioSubmit = async () => {
try
{
try
{
loading
.
value
=
true
;
loading
.
value
=
true
;
await
audioSplit
();
await
audioSplit
();
let
params
=
filterFiled
();
let
type
=
''
;
if
(
route
.
query
.
type
==
'edit_drafts'
)
{
type
=
'edit_drafts'
;
}
let
params
=
filterFiled
(
type
);
let
res
:
any
=
await
createLiveTask
(
params
);
let
res
:
any
=
await
createLiveTask
(
params
);
if
(
res
.
code
==
0
)
{
if
(
res
.
code
==
0
)
{
console
.
log
(
'创建成功-'
,
res
.
data
.
id
);
console
.
log
(
'创建成功-'
,
res
.
data
.
id
);
...
...
src/service/CreateLive.ts
View file @
6f2f1890
import
{
mergedArray
}
from
'@/utils/tool'
;
// 创建直播时提交的变量名
// 创建直播时提交的变量名
export
const
createLiveKeys
=
{
export
const
createLiveKeys
=
{
id_type
:
'id_type'
,
// 数字人库类型(数字人库和我的数字人)
id_type
:
'id_type'
,
// 数字人库类型(数字人库和我的数字人)
...
@@ -34,3 +35,18 @@ export const scriptTypeList = [
...
@@ -34,3 +35,18 @@ export const scriptTypeList = [
// 音色类型
// 音色类型
export
const
typeTones
=
1
;
// 音调
export
const
typeTones
=
1
;
// 音调
export
const
typeSoundColor
=
2
;
// 音色
export
const
typeSoundColor
=
2
;
// 音色
// 合并同类项音频
export
const
mergeSameAudio
=
(
content
:
any
[])
=>
{
let
list
=
mergedArray
(
content
);
return
list
.
map
((
item
:
any
)
=>
{
item
.
forEach
((
it
:
any
)
=>
{
if
(
it
.
audio_url
)
{
it
.
url
=
it
.
audio_url
;
}
});
return
{
data
:
item
,
};
});
};
src/store/modules/navbar.ts
View file @
6f2f1890
...
@@ -75,7 +75,6 @@ const mutations = {
...
@@ -75,7 +75,6 @@ const mutations = {
if
(
cacheIndex
!==
-
1
)
{
if
(
cacheIndex
!==
-
1
)
{
state
.
keepAliveList
[
cacheIndex
].
cache
=
true
;
state
.
keepAliveList
[
cacheIndex
].
cache
=
true
;
}
}
console
.
log
(
state
.
navbarList
);
},
},
deleteNavbar
(
state
:
StateType
,
info
:
any
)
{
deleteNavbar
(
state
:
StateType
,
info
:
any
)
{
...
...
src/utils/tool.ts
View file @
6f2f1890
...
@@ -164,13 +164,16 @@ export const deepCopy = (value: any) => {
...
@@ -164,13 +164,16 @@ export const deepCopy = (value: any) => {
// 递归深拷贝
// 递归深拷贝
export
const
ecursionDeepCopy
=
(
value
:
any
)
=>
{
export
const
ecursionDeepCopy
=
(
value
:
any
)
=>
{
if
(
!
DataType
(
value
,
'array'
)
&&
!
DataType
(
value
,
'object'
))
{
return
value
;
}
// 创建一个新的目标对象或数组
// 创建一个新的目标对象或数组
let
copy
=
Array
.
isArray
(
value
)
?
[]
:
{};
let
copy
=
DataType
(
value
,
'array'
)
?
[]
:
{};
// 遍历原对象或数组的属性
// 遍历原对象或数组的属性
for
(
let
key
in
value
)
{
for
(
let
key
in
value
)
{
// 递归调用深拷贝函数复制每个属性的值
// 递归调用深拷贝函数复制每个属性的值
copy
[
key
]
=
d
eepCopy
(
value
[
key
]);
copy
[
key
]
=
ecursionD
eepCopy
(
value
[
key
]);
}
}
return
copy
;
return
copy
;
};
};
...
@@ -387,7 +390,6 @@ export function timeComparison() {
...
@@ -387,7 +390,6 @@ export function timeComparison() {
// 获取音频文件的时长
// 获取音频文件的时长
export
const
getDurationOfAudioFile
=
(
file
:
File
)
=>
{
export
const
getDurationOfAudioFile
=
(
file
:
File
)
=>
{
console
.
log
(
file
);
return
new
Promise
((
resolve
,
reject
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
const
audio
=
new
Audio
();
const
audio
=
new
Audio
();
audio
.
src
=
URL
.
createObjectURL
(
file
);
audio
.
src
=
URL
.
createObjectURL
(
file
);
...
@@ -401,3 +403,31 @@ export const getDurationOfAudioFile = (file: File) => {
...
@@ -401,3 +403,31 @@ export const getDurationOfAudioFile = (file: File) => {
};
};
});
});
};
};
// 从二维数组中合并同类
export
const
mergedArray
=
(
arr
:
any
[],
key
:
string
=
'uuid'
,
first
:
string
=
'is_old'
)
=>
{
const
list
=
arr
.
reduce
((
result
,
subArray
)
=>
{
let
newSubArray
=
ecursionDeepCopy
(
subArray
);
// 先找出first
const
existingObj
=
newSubArray
.
filter
((
item
)
=>
item
[
first
]);
existingObj
.
forEach
((
item
:
any
)
=>
{
item
.
children
=
[];
});
if
(
existingObj
.
length
)
{
result
.
push
(...
existingObj
);
}
newSubArray
.
forEach
((
obj
)
=>
{
const
existingIndex
=
result
.
findIndex
((
item
)
=>
item
.
uuid
===
obj
.
uuid
&&
!
obj
[
first
]
&&
!
obj
.
removed
);
// console.log(obj, 'obj');
// console.log(existingIndex, 'index');
if
(
existingIndex
!==
-
1
)
{
// 标记
obj
.
removed
=
true
;
result
[
existingIndex
].
children
.
push
(
obj
);
}
});
return
result
;
},
[]);
// 防止对象引用重复
return
ecursionDeepCopy
([
list
]);
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment