feat: add support for series
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import TracksTable from "./tracks_table.mjs"
|
||||
import IntegrityManager from "./integrity_manager.mjs"
|
||||
import { updateObjectFromJoinedKey } from "./utils.mjs"
|
||||
import { loadMetadata, SeriesMetadata } from "./metadata.mjs"
|
||||
|
||||
export default class Editor {
|
||||
constructor() {
|
||||
@ -18,10 +19,11 @@ export default class Editor {
|
||||
subtitle: new TracksTable(this, "subtitle", "subtitle-tracks", "subtitle_tracks")
|
||||
}
|
||||
|
||||
this.metadata = null
|
||||
this.data = {}
|
||||
this.dirty = false
|
||||
|
||||
this.integrity_mgr = new IntegrityManager(this)
|
||||
this.integrityMgr = new IntegrityManager(this)
|
||||
|
||||
document.getElementById("check-integrity").addEventListener("click", () => this.checkIntegrity())
|
||||
document.getElementById("improve-all").addEventListener("click", () => this.improveAllNames())
|
||||
@ -29,6 +31,14 @@ export default class Editor {
|
||||
document.getElementById("reload").addEventListener("click", () => window.location.reload())
|
||||
document.getElementById("toggle-notifs").addEventListener("click", () => this.toggleNotifications())
|
||||
document.getElementById("close-notifs").addEventListener("click", () => this.closeNotifications())
|
||||
document.getElementById("prev-episode").addEventListener("click", () => this.prevEpisode())
|
||||
document.getElementById("next-episode").addEventListener("click", () => this.nextEpisode())
|
||||
|
||||
this.titleInput = document.getElementById("title")
|
||||
this.titleInput.addEventListener("change", () => {
|
||||
this.data.title = this.titleInput.value
|
||||
this.setDirty()
|
||||
})
|
||||
|
||||
this.setup()
|
||||
}
|
||||
@ -46,14 +56,27 @@ export default class Editor {
|
||||
return null
|
||||
}).then(res => {
|
||||
if (res !== null) {
|
||||
this.data = res
|
||||
this.metadata = loadMetadata(res)
|
||||
this.displayData()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
displayData() {
|
||||
document.getElementById("title").value = this.data.title
|
||||
const seriesToolbar = document.getElementById("series-toolbar")
|
||||
if (this.metadata instanceof SeriesMetadata) {
|
||||
seriesToolbar.classList.add("show")
|
||||
const cur = this.metadata.episodeIdx + 1
|
||||
const tot = this.metadata.episodes.length
|
||||
const epMeta = this.metadata.getCurrentEpisode()
|
||||
const season = epMeta.season
|
||||
const episode = epMeta.episode
|
||||
seriesToolbar.querySelector("#cur-episode").innerText = `S${season}E${episode} (${cur} / ${tot})`
|
||||
} else {
|
||||
seriesToolbar.classList.remove("show")
|
||||
}
|
||||
this.data = this.metadata.getData()
|
||||
this.titleInput.value = this.data.title
|
||||
this.tables.audio.loadTracks(this.data.audio_tracks)
|
||||
this.tables.subtitle.loadTracks(this.data.subtitle_tracks)
|
||||
}
|
||||
@ -61,7 +84,7 @@ export default class Editor {
|
||||
save() {
|
||||
fetch(`/api/file/${this.filename}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(this.data),
|
||||
body: JSON.stringify(this.metadata.data),
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
@ -100,13 +123,13 @@ export default class Editor {
|
||||
}
|
||||
|
||||
checkIntegrity() {
|
||||
if (this.integrity_mgr.checkIntegrity()) {
|
||||
if (this.integrityMgr.checkIntegrity()) {
|
||||
this.notify("No integrity error detected !", "success")
|
||||
}
|
||||
}
|
||||
|
||||
improveAllNames() {
|
||||
this.integrity_mgr.improveAllNames()
|
||||
this.integrityMgr.improveAllNames()
|
||||
this.notify("Improved all names !", "success")
|
||||
}
|
||||
|
||||
@ -128,4 +151,20 @@ export default class Editor {
|
||||
const hist = document.getElementById("notifs-hist")
|
||||
hist.classList.remove("show")
|
||||
}
|
||||
|
||||
prevEpisode() {
|
||||
if (this.metadata instanceof SeriesMetadata) {
|
||||
if (this.metadata.prev()) {
|
||||
this.displayData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextEpisode() {
|
||||
if (this.metadata instanceof SeriesMetadata) {
|
||||
if (this.metadata.next()) {
|
||||
this.displayData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ function addOptions(files) {
|
||||
defaultOpt.innerText = "----- Select a file -----"
|
||||
defaultOpt.value = ""
|
||||
select.appendChild(defaultOpt)
|
||||
files.forEach(file => {
|
||||
files.sort().forEach(file => {
|
||||
const option = document.createElement("option")
|
||||
option.innerText = file
|
||||
option.value = file
|
||||
|
5
editor/public/static/js/media_file.mjs
Normal file
5
editor/public/static/js/media_file.mjs
Normal file
@ -0,0 +1,5 @@
|
||||
export default class MediaFile {
|
||||
constructor(data) {
|
||||
|
||||
}
|
||||
}
|
85
editor/public/static/js/metadata.mjs
Normal file
85
editor/public/static/js/metadata.mjs
Normal file
@ -0,0 +1,85 @@
|
||||
export default class Metadata {
|
||||
constructor(data) {
|
||||
this.data = data
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.data
|
||||
}
|
||||
}
|
||||
|
||||
export class MediaMetadata extends Metadata {
|
||||
constructor(data) {
|
||||
super(data)
|
||||
}
|
||||
}
|
||||
|
||||
export class EpisodeMetadata extends MediaMetadata {
|
||||
REGEXP = /s(?<season>\d+)e(?<episode>\d+)/i
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} data
|
||||
* @param {string} episodeKey
|
||||
*/
|
||||
constructor(data, episodeKey) {
|
||||
super(data)
|
||||
this.key = episodeKey
|
||||
|
||||
let m = this.key.match(this.REGEXP) ?? this.data.filename.match(this.REGEXP)
|
||||
this.season = "xx"
|
||||
this.episode = "xx"
|
||||
if (m) {
|
||||
this.season = m.groups.season
|
||||
this.episode = m.groups.episode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SeriesMetadata extends Metadata {
|
||||
constructor(data) {
|
||||
super(data)
|
||||
const episodeKeys = Object.keys(data).sort()
|
||||
this.episodes = episodeKeys.map(key => {
|
||||
return new EpisodeMetadata(data[key], key)
|
||||
})
|
||||
|
||||
this.episodeIdx = 0
|
||||
}
|
||||
|
||||
getCurrentEpisode() {
|
||||
return this.episodes[this.episodeIdx]
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.getCurrentEpisode().getData()
|
||||
}
|
||||
|
||||
prev() {
|
||||
if (this.episodeIdx === 0) {
|
||||
return false
|
||||
}
|
||||
this.episodeIdx -= 1
|
||||
return true
|
||||
}
|
||||
|
||||
next() {
|
||||
if (this.episodeIdx === this.episodes.length - 1) {
|
||||
return false
|
||||
}
|
||||
this.episodeIdx += 1
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} data
|
||||
* @returns {Metadata}
|
||||
*/
|
||||
export function loadMetadata(data) {
|
||||
if ("filename" in data) {
|
||||
return new MediaMetadata(data)
|
||||
}
|
||||
return new SeriesMetadata(data)
|
||||
}
|
@ -113,6 +113,7 @@ export class Track {
|
||||
"warning"
|
||||
)
|
||||
value = lang
|
||||
this.editValue(field.key, value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,19 +141,21 @@ export class Track {
|
||||
this.table.editTrack(this.idx, key, value)
|
||||
|
||||
const input = this.row.querySelector(`[data-key='${key}']`)
|
||||
const fieldType = this.table.getFieldProps(key).type
|
||||
switch (fieldType) {
|
||||
case "bool":
|
||||
input.checked = value
|
||||
break
|
||||
default:
|
||||
input.value = value
|
||||
break
|
||||
if (input) {
|
||||
const fieldType = this.table.getFieldProps(key).type
|
||||
switch (fieldType) {
|
||||
case "bool":
|
||||
input.checked = value
|
||||
break
|
||||
default:
|
||||
input.value = value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
improveName() {
|
||||
this.table.editor.integrity_mgr.improveName(this)
|
||||
this.table.editor.integrityMgr.improveName(this)
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,9 +187,10 @@ export default class TracksTable {
|
||||
this.table = document.getElementById(tableId)
|
||||
this.headers = this.table.querySelector("thead tr")
|
||||
this.body = this.table.querySelector("tbody")
|
||||
this.fields = []
|
||||
this.tracks = []
|
||||
this.dataKey = dataKey
|
||||
|
||||
this.tracks = []
|
||||
this.fields = []
|
||||
this.onehots = {}
|
||||
}
|
||||
|
||||
@ -195,8 +199,8 @@ export default class TracksTable {
|
||||
}
|
||||
|
||||
loadTracks(tracks) {
|
||||
this.tracks = tracks.map((t, i) => new Track(this, i, t))
|
||||
this.clear()
|
||||
this.tracks = tracks.map((t, i) => new Track(this, i, t))
|
||||
if (tracks.length === 0) {
|
||||
return
|
||||
}
|
||||
@ -208,9 +212,11 @@ export default class TracksTable {
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.tracks = []
|
||||
this.fields = []
|
||||
this.onehots = {}
|
||||
this.headers.innerHTML = ""
|
||||
this.body.innerHTML = ""
|
||||
this.fields = []
|
||||
}
|
||||
|
||||
detectFields() {
|
||||
|
@ -41,6 +41,11 @@ export const LANGUAGES = {
|
||||
code: "de",
|
||||
aliases: ["de", "ger", "german", "allemand", "deutsch", "germany", "allemagne"]
|
||||
},
|
||||
"ita": {
|
||||
display: "Italiano",
|
||||
code: "it",
|
||||
aliases: ["it", "ita", "italian", "italien", "italiano", "italy", "italie"]
|
||||
},
|
||||
"kor": {
|
||||
display: "Korean",
|
||||
code: "kr",
|
||||
@ -64,7 +69,7 @@ export const LANGUAGES = {
|
||||
}
|
||||
|
||||
export function getLanguageAliases(langTag) {
|
||||
return (langTag === "und" ? [] : [langTag]).concat(LANGUAGES[langTag].aliases)
|
||||
return [langTag].concat(LANGUAGES[langTag].aliases)
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user