|
@@ -12,6 +12,7 @@ import Toast from "toasters";
|
|
|
import aw from "@/aw";
|
|
|
import validation from "@/validation";
|
|
|
import keyboardShortcuts from "@/keyboardShortcuts";
|
|
|
+import { useForm } from "@/composables/useForm";
|
|
|
|
|
|
import { Song } from "@/types/song.js";
|
|
|
|
|
@@ -66,7 +67,6 @@ const {
|
|
|
song,
|
|
|
youtubeId,
|
|
|
prefillData,
|
|
|
- originalSong,
|
|
|
reports,
|
|
|
newSong,
|
|
|
bulk,
|
|
@@ -84,9 +84,6 @@ const youtubeVideoNote = ref("");
|
|
|
const useHTTPS = ref(false);
|
|
|
const muted = ref(false);
|
|
|
const volumeSliderValue = ref(0);
|
|
|
-const artistInputValue = ref("");
|
|
|
-const genreInputValue = ref("");
|
|
|
-const tagInputValue = ref("");
|
|
|
const activityWatchVideoDataInterval = ref(null);
|
|
|
const activityWatchVideoLastStatus = ref("");
|
|
|
const activityWatchVideoLastStartDuration = ref(0);
|
|
@@ -137,12 +134,12 @@ const thumbnailWidth = ref(null);
|
|
|
const thumbnailHeight = ref(null);
|
|
|
const thumbnailLoadError = ref(false);
|
|
|
const tabs = ref([]);
|
|
|
-const inputs = ref([]);
|
|
|
const playerReady = ref(true);
|
|
|
const interval = ref();
|
|
|
const saveButtonRefs = ref(<any>[]);
|
|
|
const canvasElement = ref();
|
|
|
const genreHelper = ref();
|
|
|
+const saveButtonRefName = ref();
|
|
|
// EditSongs
|
|
|
const items = ref([]);
|
|
|
const currentSong = ref(<Song>{});
|
|
@@ -196,7 +193,6 @@ const {
|
|
|
pauseVideo,
|
|
|
setSong,
|
|
|
resetSong,
|
|
|
- updateOriginalSong,
|
|
|
updateSongField,
|
|
|
updateReports,
|
|
|
setPlaybackRate
|
|
@@ -300,12 +296,12 @@ const onSavedError = youtubeId => {
|
|
|
if (itemIndex > -1) items.value[itemIndex].status = "error";
|
|
|
};
|
|
|
|
|
|
-const onSaving = youtubeId => {
|
|
|
- const itemIndex = items.value.findIndex(
|
|
|
- item => item.song.youtubeId === youtubeId
|
|
|
- );
|
|
|
- if (itemIndex > -1) items.value[itemIndex].status = "saving";
|
|
|
-};
|
|
|
+// const onSaving = youtubeId => {
|
|
|
+// const itemIndex = items.value.findIndex(
|
|
|
+// item => item.song.youtubeId === youtubeId
|
|
|
+// );
|
|
|
+// if (itemIndex > -1) items.value[itemIndex].status = "saving";
|
|
|
+// };
|
|
|
// EditSongs end
|
|
|
|
|
|
const onThumbnailLoad = () => {
|
|
@@ -327,6 +323,167 @@ const onThumbnailLoadError = error => {
|
|
|
thumbnailLoadError.value = error !== 0;
|
|
|
};
|
|
|
|
|
|
+const { inputs, unsavedChanges, save, setValue, setOriginalValue } = useForm(
|
|
|
+ {
|
|
|
+ title: {
|
|
|
+ value: "",
|
|
|
+ validate: value => {
|
|
|
+ if (!validation.isLength(value, 1, 100))
|
|
|
+ return "Title must have between 1 and 100 characters.";
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ duration: {
|
|
|
+ value: 0,
|
|
|
+ validate: value => {
|
|
|
+ if (
|
|
|
+ Number(inputs.value.skipDuration.value) + Number(value) >
|
|
|
+ Number.parseInt(youtubeVideoDuration.value) &&
|
|
|
+ (((!newSong.value || bulk.value) && !youtubeError.value) ||
|
|
|
+ inputs.value.duration.originalValue !== value)
|
|
|
+ )
|
|
|
+ return "Duration can't be higher than the length of the video";
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ skipDuration: 0,
|
|
|
+ thumbnail: {
|
|
|
+ value: "",
|
|
|
+ validate: value => {
|
|
|
+ if (!validation.isLength(value, 8, 256))
|
|
|
+ return "Thumbnail must have between 8 and 256 characters.";
|
|
|
+ if (useHTTPS.value && value.indexOf("https://") !== 0)
|
|
|
+ return 'Thumbnail must start with "https://".';
|
|
|
+ if (
|
|
|
+ !useHTTPS.value &&
|
|
|
+ value.indexOf("https://") !== 0 &&
|
|
|
+ value.indexOf("http://") !== 0
|
|
|
+ )
|
|
|
+ return 'Thumbnail must start with "http(s)://".';
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ youtubeId: {
|
|
|
+ value: "",
|
|
|
+ validate: value => {
|
|
|
+ if (
|
|
|
+ !newSong.value &&
|
|
|
+ youtubeError.value &&
|
|
|
+ inputs.value.youtubeId.originalValue !== value
|
|
|
+ )
|
|
|
+ return "You're not allowed to change the YouTube id while the player is not working";
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ verified: false,
|
|
|
+ addArtist: "",
|
|
|
+ artists: {
|
|
|
+ value: [],
|
|
|
+ validate: value => {
|
|
|
+ if (
|
|
|
+ (inputs.value.verified.value && value.length < 1) ||
|
|
|
+ value.length > 10
|
|
|
+ )
|
|
|
+ return "Invalid artists. You must have at least 1 artist and a maximum of 10 artists.";
|
|
|
+
|
|
|
+ let error;
|
|
|
+ value.forEach(artist => {
|
|
|
+ if (!validation.isLength(artist, 1, 64))
|
|
|
+ error = "Artist must have between 1 and 64 characters.";
|
|
|
+ if (artist === "NONE")
|
|
|
+ error =
|
|
|
+ 'Invalid artist format. Artists are not allowed to be named "NONE".';
|
|
|
+ });
|
|
|
+ return error || true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addGenre: "",
|
|
|
+ genres: {
|
|
|
+ value: [],
|
|
|
+ validate: value => {
|
|
|
+ if (
|
|
|
+ (inputs.value.verified.value && value.length < 1) ||
|
|
|
+ value.length > 16
|
|
|
+ )
|
|
|
+ return "Invalid genres. You must have between 1 and 16 genres.";
|
|
|
+
|
|
|
+ let error;
|
|
|
+ value.forEach(genre => {
|
|
|
+ if (!validation.isLength(genre, 1, 32))
|
|
|
+ error = "Genre must have between 1 and 32 characters.";
|
|
|
+ if (!validation.regex.ascii.test(genre))
|
|
|
+ error =
|
|
|
+ "Invalid genre format. Only ascii characters are allowed.";
|
|
|
+ });
|
|
|
+ return error || true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addTag: "",
|
|
|
+ tags: {
|
|
|
+ value: [],
|
|
|
+ validate: value => {
|
|
|
+ let error;
|
|
|
+ value.forEach(tag => {
|
|
|
+ if (
|
|
|
+ !/^[a-zA-Z0-9_]{1,64}$|^[a-zA-Z0-9_]{1,64}\[[a-zA-Z0-9_]{1,64}\]$/.test(
|
|
|
+ tag
|
|
|
+ )
|
|
|
+ )
|
|
|
+ error = "Invalid tag format.";
|
|
|
+ });
|
|
|
+ return error || true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (status, message, values) =>
|
|
|
+ new Promise((resolve, reject) => {
|
|
|
+ const saveButtonRef = saveButtonRefs.value[saveButtonRefName.value];
|
|
|
+ const mergedValues = {
|
|
|
+ ...JSON.parse(JSON.stringify(song.value)),
|
|
|
+ ...values
|
|
|
+ };
|
|
|
+ if (status === "success") {
|
|
|
+ const cb = res => {
|
|
|
+ if (res.status === "error") {
|
|
|
+ reject(new Error(res.message));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ new Toast(res.message);
|
|
|
+ saveButtonRef.handleSuccessfulSave();
|
|
|
+ onSavedSuccess(values.youtubeId);
|
|
|
+ if (!newSong.value) setSong(mergedValues);
|
|
|
+ };
|
|
|
+ if (newSong.value)
|
|
|
+ socket.dispatch("songs.create", mergedValues, cb);
|
|
|
+ else
|
|
|
+ socket.dispatch(
|
|
|
+ "songs.update",
|
|
|
+ song.value._id,
|
|
|
+ mergedValues,
|
|
|
+ cb
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ new Toast(message);
|
|
|
+ if (status === "unchanged" && !newSong.value) {
|
|
|
+ saveButtonRef.handleSuccessfulSave();
|
|
|
+ onSavedSuccess(values.youtubeId);
|
|
|
+ } else {
|
|
|
+ saveButtonRef.handleFailedSave();
|
|
|
+ onSavedError(values.youtubeId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ { modalUuid: props.modalUuid, preventCloseUnsaved: false }
|
|
|
+);
|
|
|
+
|
|
|
+const saveSong = (refName: string, closeOrNext?: boolean) => {
|
|
|
+ saveButtonRefName.value = refName;
|
|
|
+ save(() => {
|
|
|
+ if (closeOrNext && bulk.value) editNextSong();
|
|
|
+ else if (closeOrNext) closeCurrentModal();
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
const unloadSong = (_youtubeId, songId?) => {
|
|
|
songDataLoaded.value = false;
|
|
|
songDeleted.value = false;
|
|
@@ -345,7 +502,7 @@ const unloadSong = (_youtubeId, songId?) => {
|
|
|
saveButtonRefs.value.saveButton.status = "default";
|
|
|
};
|
|
|
|
|
|
-const loadSong = _youtubeId => {
|
|
|
+const loadSong = (_youtubeId: string, reset?: boolean) => {
|
|
|
console.log(`LOAD SONG ${_youtubeId}`);
|
|
|
songNotFound.value = false;
|
|
|
socket.dispatch(`songs.getSongsFromYoutubeIds`, [_youtubeId], res => {
|
|
@@ -354,7 +511,7 @@ const loadSong = _youtubeId => {
|
|
|
let _song = songs[0];
|
|
|
_song = Object.assign(_song, prefillData.value);
|
|
|
|
|
|
- setSong(_song);
|
|
|
+ setSong(_song, reset);
|
|
|
|
|
|
songDataLoaded.value = true;
|
|
|
|
|
@@ -428,232 +585,6 @@ const seekTo = position => {
|
|
|
video.value.player.seekTo(position);
|
|
|
};
|
|
|
|
|
|
-const save = (songToCopy, closeOrNext, saveButtonRefName, _newSong = false) => {
|
|
|
- const _song = JSON.parse(JSON.stringify(songToCopy));
|
|
|
-
|
|
|
- if (!newSong.value || bulk.value) onSaving(_song.youtubeId);
|
|
|
-
|
|
|
- const saveButtonRef = saveButtonRefs.value[saveButtonRefName];
|
|
|
-
|
|
|
- if (!youtubeError.value && youtubeVideoDuration.value === "0.000") {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong) onSavedError(_song.youtubeId);
|
|
|
- return new Toast("The video appears to not be working.");
|
|
|
- }
|
|
|
-
|
|
|
- if (!_song.title) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast("Please fill in all fields");
|
|
|
- }
|
|
|
-
|
|
|
- if (!_song.thumbnail) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast("Please fill in all fields");
|
|
|
- }
|
|
|
-
|
|
|
- // const thumbnailHeight = thumbnailElement.value.naturalHeight;
|
|
|
- // const thumbnailWidth = thumbnailElement.value.naturalWidth;
|
|
|
-
|
|
|
- // if (thumbnailHeight < 80 || thumbnailWidth < 80) {
|
|
|
- // saveButtonRef.handleFailedSave();
|
|
|
- // return new Toast(
|
|
|
- // "Thumbnail width and height must be at least 80px."
|
|
|
- // );
|
|
|
- // }
|
|
|
-
|
|
|
- // if (thumbnailHeight > 4000 || thumbnailWidth > 4000) {
|
|
|
- // saveButtonRef.handleFailedSave();
|
|
|
- // return new Toast(
|
|
|
- // "Thumbnail width and height must be less than 4000px."
|
|
|
- // );
|
|
|
- // }
|
|
|
-
|
|
|
- // if (thumbnailHeight - thumbnailWidth > 5) {
|
|
|
- // saveButtonRef.handleFailedSave();
|
|
|
- // return new Toast("Thumbnail cannot be taller than it is wide.");
|
|
|
- // }
|
|
|
-
|
|
|
- // Youtube Id
|
|
|
- if (
|
|
|
- !_newSong &&
|
|
|
- youtubeError.value &&
|
|
|
- originalSong.value.youtubeId !== _song.youtubeId
|
|
|
- ) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast(
|
|
|
- "You're not allowed to change the YouTube id while the player is not working"
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- // Duration
|
|
|
- if (
|
|
|
- Number(_song.skipDuration) + Number(_song.duration) >
|
|
|
- Number.parseInt(youtubeVideoDuration.value) &&
|
|
|
- (((!_newSong || bulk.value) && !youtubeError.value) ||
|
|
|
- originalSong.value.duration !== _song.duration)
|
|
|
- ) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast(
|
|
|
- "Duration can't be higher than the length of the video"
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- // Title
|
|
|
- if (!validation.isLength(_song.title, 1, 100)) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast("Title must have between 1 and 100 characters.");
|
|
|
- }
|
|
|
-
|
|
|
- // Artists
|
|
|
- if (
|
|
|
- (_song.verified && _song.artists.length < 1) ||
|
|
|
- _song.artists.length > 10
|
|
|
- ) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast(
|
|
|
- "Invalid artists. You must have at least 1 artist and a maximum of 10 artists."
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- let error;
|
|
|
- _song.artists.forEach(artist => {
|
|
|
- if (!validation.isLength(artist, 1, 64)) {
|
|
|
- error = "Artist must have between 1 and 64 characters.";
|
|
|
- return error;
|
|
|
- }
|
|
|
- if (artist === "NONE") {
|
|
|
- error =
|
|
|
- 'Invalid artist format. Artists are not allowed to be named "NONE".';
|
|
|
- return error;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- });
|
|
|
-
|
|
|
- if (error) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast(error);
|
|
|
- }
|
|
|
-
|
|
|
- // Genres
|
|
|
- error = undefined;
|
|
|
- if (_song.verified && _song.genres.length < 1)
|
|
|
- _song.genres.forEach(genre => {
|
|
|
- if (!validation.isLength(genre, 1, 32)) {
|
|
|
- error = "Genre must have between 1 and 32 characters.";
|
|
|
- return error;
|
|
|
- }
|
|
|
- if (!validation.regex.ascii.test(genre)) {
|
|
|
- error =
|
|
|
- "Invalid genre format. Only ascii characters are allowed.";
|
|
|
- return error;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- });
|
|
|
-
|
|
|
- if ((_song.verified && _song.genres.length < 1) || _song.genres.length > 16)
|
|
|
- error = "You must have between 1 and 16 genres.";
|
|
|
-
|
|
|
- if (error) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast(error);
|
|
|
- }
|
|
|
-
|
|
|
- error = undefined;
|
|
|
- _song.tags.forEach(tag => {
|
|
|
- if (
|
|
|
- !/^[a-zA-Z0-9_]{1,64}$|^[a-zA-Z0-9_]{1,64}\[[a-zA-Z0-9_]{1,64}\]$/.test(
|
|
|
- tag
|
|
|
- )
|
|
|
- ) {
|
|
|
- error = "Invalid tag format.";
|
|
|
- return error;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- });
|
|
|
-
|
|
|
- if (error) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast(error);
|
|
|
- }
|
|
|
-
|
|
|
- // Thumbnail
|
|
|
- if (!validation.isLength(_song.thumbnail, 1, 256)) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast("Thumbnail must have between 8 and 256 characters.");
|
|
|
- }
|
|
|
- if (useHTTPS.value && _song.thumbnail.indexOf("https://") !== 0) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast('Thumbnail must start with "https://".');
|
|
|
- }
|
|
|
-
|
|
|
- if (
|
|
|
- !useHTTPS.value &&
|
|
|
- _song.thumbnail.indexOf("http://") !== 0 &&
|
|
|
- _song.thumbnail.indexOf("https://") !== 0
|
|
|
- ) {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- if (!_newSong || bulk.value) onSavedError(_song.youtubeId);
|
|
|
- return new Toast('Thumbnail must start with "http://".');
|
|
|
- }
|
|
|
-
|
|
|
- saveButtonRef.status = "saving";
|
|
|
-
|
|
|
- if (_newSong)
|
|
|
- return socket.dispatch(`songs.create`, _song, res => {
|
|
|
- new Toast(res.message);
|
|
|
-
|
|
|
- if (res.status === "error") {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- onSavedError(_song.youtubeId);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- saveButtonRef.handleSuccessfulSave();
|
|
|
- onSavedSuccess(_song.youtubeId);
|
|
|
-
|
|
|
- if (!closeOrNext) {
|
|
|
- loadSong(_song.youtubeId);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (bulk.value) editNextSong();
|
|
|
- else closeCurrentModal();
|
|
|
- });
|
|
|
- return socket.dispatch(`songs.update`, _song._id, _song, res => {
|
|
|
- new Toast(res.message);
|
|
|
-
|
|
|
- if (res.status === "error") {
|
|
|
- saveButtonRef.handleFailedSave();
|
|
|
- onSavedError(_song.youtubeId);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- updateOriginalSong(_song);
|
|
|
-
|
|
|
- saveButtonRef.handleSuccessfulSave();
|
|
|
- onSavedSuccess(_song.youtubeId);
|
|
|
-
|
|
|
- if (!closeOrNext) return;
|
|
|
-
|
|
|
- if (bulk.value) editNextSong();
|
|
|
- else closeCurrentModal();
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
const getAlbumData = type => {
|
|
|
if (!song.value.discogs) return;
|
|
|
if (type === "title")
|
|
@@ -704,7 +635,7 @@ const getYouTubeData = type => {
|
|
|
try {
|
|
|
const { author } = video.value.player.getVideoData();
|
|
|
|
|
|
- if (author) artistInputValue.value = author;
|
|
|
+ if (author) setValue({ addArtist: author });
|
|
|
else throw new Error("No video author found");
|
|
|
} catch (e) {
|
|
|
new Toast(
|
|
@@ -776,40 +707,40 @@ const toggleMute = () => {
|
|
|
|
|
|
const addTag = (type, value?) => {
|
|
|
if (type === "genres") {
|
|
|
- const genre = value || genreInputValue.value.trim();
|
|
|
+ const genre = value || inputs.value.addGenre.value.trim();
|
|
|
|
|
|
if (
|
|
|
- song.value.genres
|
|
|
+ inputs.value.genres.value
|
|
|
.map(genre => genre.toLowerCase())
|
|
|
.indexOf(genre.toLowerCase()) !== -1
|
|
|
)
|
|
|
return new Toast("Genre already exists");
|
|
|
if (genre) {
|
|
|
- song.value.genres.push(genre);
|
|
|
- genreInputValue.value = "";
|
|
|
+ inputs.value.genres.value.push(genre);
|
|
|
+ inputs.value.addGenre.value = "";
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return new Toast("Genre cannot be empty");
|
|
|
}
|
|
|
if (type === "artists") {
|
|
|
- const artist = value || artistInputValue.value;
|
|
|
- if (song.value.artists.indexOf(artist) !== -1)
|
|
|
+ const artist = value || inputs.value.addArtist.value.trim();
|
|
|
+ if (inputs.value.artists.value.indexOf(artist) !== -1)
|
|
|
return new Toast("Artist already exists");
|
|
|
if (artist !== "") {
|
|
|
- song.value.artists.push(artist);
|
|
|
- artistInputValue.value = "";
|
|
|
+ inputs.value.artists.value.push(artist);
|
|
|
+ inputs.value.addArtist.value = "";
|
|
|
return false;
|
|
|
}
|
|
|
return new Toast("Artist cannot be empty");
|
|
|
}
|
|
|
if (type === "tags") {
|
|
|
- const tag = value || tagInputValue.value;
|
|
|
- if (song.value.tags.indexOf(tag) !== -1)
|
|
|
+ const tag = value || inputs.value.addTag.value.trim();
|
|
|
+ if (inputs.value.tags.value.indexOf(tag) !== -1)
|
|
|
return new Toast("Tag already exists");
|
|
|
if (tag !== "") {
|
|
|
- song.value.tags.push(tag);
|
|
|
- tagInputValue.value = "";
|
|
|
+ inputs.value.tags.value.push(tag);
|
|
|
+ inputs.value.addTag.value = "";
|
|
|
return false;
|
|
|
}
|
|
|
return new Toast("Tag cannot be empty");
|
|
@@ -820,11 +751,20 @@ const addTag = (type, value?) => {
|
|
|
|
|
|
const removeTag = (type, value) => {
|
|
|
if (type === "genres")
|
|
|
- song.value.genres.splice(song.value.genres.indexOf(value), 1);
|
|
|
+ inputs.value.genres.value.splice(
|
|
|
+ inputs.value.genres.value.indexOf(value),
|
|
|
+ 1
|
|
|
+ );
|
|
|
else if (type === "artists")
|
|
|
- song.value.artists.splice(song.value.artists.indexOf(value), 1);
|
|
|
+ inputs.value.artists.value.splice(
|
|
|
+ inputs.value.artists.value.indexOf(value),
|
|
|
+ 1
|
|
|
+ );
|
|
|
else if (type === "tags")
|
|
|
- song.value.tags.splice(song.value.tags.indexOf(value), 1);
|
|
|
+ inputs.value.tags.value.splice(
|
|
|
+ inputs.value.tags.value.indexOf(value),
|
|
|
+ 1
|
|
|
+ );
|
|
|
};
|
|
|
|
|
|
const setTrackPosition = event => {
|
|
@@ -911,23 +851,9 @@ const confirmAction = ({ message, action, params }) => {
|
|
|
|
|
|
const onCloseModal = (): Promise<void> =>
|
|
|
new Promise(resolve => {
|
|
|
- const songStringified = JSON.stringify({
|
|
|
- ...song.value,
|
|
|
- ...{
|
|
|
- duration: Number(song.value.duration).toFixed(3)
|
|
|
- }
|
|
|
- });
|
|
|
- const originalSongStringified = JSON.stringify({
|
|
|
- ...originalSong.value,
|
|
|
- ...{
|
|
|
- duration: Number(originalSong.value.duration).toFixed(3)
|
|
|
- }
|
|
|
- });
|
|
|
- const unsavedChanges = songStringified !== originalSongStringified;
|
|
|
-
|
|
|
const confirmReasons = [];
|
|
|
|
|
|
- if (unsavedChanges) {
|
|
|
+ if (unsavedChanges.value.length > 0) {
|
|
|
confirmReasons.push(
|
|
|
"You have unsaved changes. Are you sure you want to discard unsaved changes?"
|
|
|
);
|
|
@@ -972,7 +898,7 @@ watch(
|
|
|
watch(youtubeId, (_youtubeId, _oldYoutubeId) => {
|
|
|
console.log("NEW YOUTUBE ID", _youtubeId);
|
|
|
if (_oldYoutubeId) unloadSong(_oldYoutubeId);
|
|
|
- if (_youtubeId) loadSong(_youtubeId);
|
|
|
+ if (_youtubeId) loadSong(_youtubeId, _youtubeId !== _oldYoutubeId);
|
|
|
});
|
|
|
watch(
|
|
|
() => hasPermission("songs.update"),
|
|
@@ -982,6 +908,13 @@ watch(
|
|
|
);
|
|
|
|
|
|
onMounted(async () => {
|
|
|
+ editSongStore.form = {
|
|
|
+ inputs,
|
|
|
+ unsavedChanges,
|
|
|
+ save,
|
|
|
+ setValue,
|
|
|
+ setOriginalValue
|
|
|
+ };
|
|
|
preventCloseCbs[props.modalUuid] = onCloseModal;
|
|
|
|
|
|
activityWatchVideoDataInterval.value = setInterval(() => {
|
|
@@ -1905,7 +1838,7 @@ onBeforeUnmount(() => {
|
|
|
!songDeleted
|
|
|
"
|
|
|
class="thumbnail-dummy"
|
|
|
- :src="song.thumbnail"
|
|
|
+ :src="inputs['thumbnail'].value"
|
|
|
ref="thumbnailElement"
|
|
|
@load="onThumbnailLoad"
|
|
|
/>
|
|
@@ -1922,10 +1855,8 @@ onBeforeUnmount(() => {
|
|
|
<input
|
|
|
class="input"
|
|
|
type="text"
|
|
|
- :ref="
|
|
|
- el => (inputs['title-input'] = el)
|
|
|
- "
|
|
|
- v-model="song.title"
|
|
|
+ :ref="el => (inputs['title'].ref = el)"
|
|
|
+ v-model="inputs['title'].value"
|
|
|
placeholder="Enter song title..."
|
|
|
@keyup.shift.enter="
|
|
|
getAlbumData('title')
|
|
@@ -1962,7 +1893,9 @@ onBeforeUnmount(() => {
|
|
|
class="input"
|
|
|
type="text"
|
|
|
placeholder="Enter song duration..."
|
|
|
- v-model.number="song.duration"
|
|
|
+ v-model.number="
|
|
|
+ inputs['duration'].value
|
|
|
+ "
|
|
|
@keyup.shift.enter="fillDuration()"
|
|
|
/>
|
|
|
<button
|
|
@@ -1986,7 +1919,9 @@ onBeforeUnmount(() => {
|
|
|
class="input"
|
|
|
type="text"
|
|
|
placeholder="Enter skip duration..."
|
|
|
- v-model.number="song.skipDuration"
|
|
|
+ v-model.number="
|
|
|
+ inputs['skipDuration'].value
|
|
|
+ "
|
|
|
/>
|
|
|
</p>
|
|
|
</div>
|
|
@@ -2024,7 +1959,7 @@ onBeforeUnmount(() => {
|
|
|
<input
|
|
|
class="input"
|
|
|
type="text"
|
|
|
- v-model="song.thumbnail"
|
|
|
+ v-model="inputs['thumbnail'].value"
|
|
|
placeholder="Enter link to thumbnail..."
|
|
|
@keyup.shift.enter="
|
|
|
getAlbumData('albumArt')
|
|
@@ -2060,7 +1995,7 @@ onBeforeUnmount(() => {
|
|
|
class="input"
|
|
|
type="text"
|
|
|
placeholder="Enter YouTube ID..."
|
|
|
- v-model="song.youtubeId"
|
|
|
+ v-model="inputs['youtubeId'].value"
|
|
|
/>
|
|
|
</p>
|
|
|
</div>
|
|
@@ -2071,7 +2006,7 @@ onBeforeUnmount(() => {
|
|
|
<input
|
|
|
type="checkbox"
|
|
|
id="verified"
|
|
|
- v-model="song.verified"
|
|
|
+ v-model="inputs['verified'].value"
|
|
|
/>
|
|
|
<span class="slider round"></span>
|
|
|
</label>
|
|
@@ -2084,7 +2019,7 @@ onBeforeUnmount(() => {
|
|
|
<label class="label">Artists</label>
|
|
|
<p class="control has-addons">
|
|
|
<auto-suggest
|
|
|
- v-model="artistInputValue"
|
|
|
+ v-model="inputs['addArtist'].value"
|
|
|
ref="new-artist"
|
|
|
placeholder="Add artist..."
|
|
|
:all-items="
|
|
@@ -2126,7 +2061,8 @@ onBeforeUnmount(() => {
|
|
|
<div class="list-container">
|
|
|
<div
|
|
|
class="list-item"
|
|
|
- v-for="artist in song.artists"
|
|
|
+ v-for="artist in inputs['artists']
|
|
|
+ .value"
|
|
|
:key="artist"
|
|
|
>
|
|
|
<div
|
|
@@ -2155,7 +2091,7 @@ onBeforeUnmount(() => {
|
|
|
</label>
|
|
|
<p class="control has-addons">
|
|
|
<auto-suggest
|
|
|
- v-model="genreInputValue"
|
|
|
+ v-model="inputs['addGenre'].value"
|
|
|
ref="new-genre"
|
|
|
placeholder="Add genre..."
|
|
|
:all-items="autosuggest.allItems.genres"
|
|
@@ -2185,7 +2121,7 @@ onBeforeUnmount(() => {
|
|
|
<div class="list-container">
|
|
|
<div
|
|
|
class="list-item"
|
|
|
- v-for="genre in song.genres"
|
|
|
+ v-for="genre in inputs['genres'].value"
|
|
|
:key="genre"
|
|
|
>
|
|
|
<div
|
|
@@ -2202,7 +2138,7 @@ onBeforeUnmount(() => {
|
|
|
<label class="label">Tags</label>
|
|
|
<p class="control has-addons">
|
|
|
<auto-suggest
|
|
|
- v-model="tagInputValue"
|
|
|
+ v-model="inputs['addTag'].value"
|
|
|
ref="new-tag"
|
|
|
placeholder="Add tag..."
|
|
|
:all-items="autosuggest.allItems.tags"
|
|
@@ -2218,7 +2154,7 @@ onBeforeUnmount(() => {
|
|
|
<div class="list-container">
|
|
|
<div
|
|
|
class="list-item"
|
|
|
- v-for="tag in song.tags"
|
|
|
+ v-for="tag in inputs['tags'].value"
|
|
|
:key="tag"
|
|
|
>
|
|
|
<div
|
|
@@ -2319,14 +2255,14 @@ onBeforeUnmount(() => {
|
|
|
<div v-if="!newSong && !songDeleted">
|
|
|
<save-button
|
|
|
:ref="el => (saveButtonRefs['saveButton'] = el)"
|
|
|
- @clicked="save(song, false, 'saveButton')"
|
|
|
+ @clicked="saveSong('saveButton')"
|
|
|
/>
|
|
|
<save-button
|
|
|
:ref="el => (saveButtonRefs['saveAndCloseButton'] = el)"
|
|
|
:default-message="
|
|
|
bulk ? `Save and next` : `Save and close`
|
|
|
"
|
|
|
- @clicked="save(song, true, 'saveAndCloseButton')"
|
|
|
+ @clicked="saveSong('saveAndCloseButton', true)"
|
|
|
/>
|
|
|
|
|
|
<div class="right">
|
|
@@ -2352,7 +2288,7 @@ onBeforeUnmount(() => {
|
|
|
<save-button
|
|
|
:ref="el => (saveButtonRefs['createButton'] = el)"
|
|
|
default-message="Create Song"
|
|
|
- @clicked="save(song, false, 'createButton', true)"
|
|
|
+ @clicked="saveSong('createButton')"
|
|
|
/>
|
|
|
<save-button
|
|
|
:ref="
|
|
@@ -2361,9 +2297,7 @@ onBeforeUnmount(() => {
|
|
|
:default-message="
|
|
|
bulk ? `Create and next` : `Create and close`
|
|
|
"
|
|
|
- @clicked="
|
|
|
- save(song, true, 'createAndCloseButton', true)
|
|
|
- "
|
|
|
+ @clicked="saveSong('createAndCloseButton', true)"
|
|
|
/>
|
|
|
</div>
|
|
|
</template>
|