1
0
mirror of https://github.com/mitb-archive/filebot synced 2025-03-09 13:59:49 -04:00

Support Kodi Naming Standard (i.e. same as Plex, but with SxE and non-range based multi-episode formatting) via {kodi} binding

This commit is contained in:
Reinhard Pointner 2018-11-18 00:50:34 +07:00
parent 9eb07913b5
commit 6e8359913b
5 changed files with 62 additions and 7 deletions

View File

@ -55,7 +55,7 @@ import net.filebot.media.FFProbe;
import net.filebot.media.ImageMetadata; import net.filebot.media.ImageMetadata;
import net.filebot.media.LocalDatasource.PhotoFile; import net.filebot.media.LocalDatasource.PhotoFile;
import net.filebot.media.MetaAttributes; import net.filebot.media.MetaAttributes;
import net.filebot.media.PlexNamingStandard; import net.filebot.media.NamingStandard;
import net.filebot.media.VideoFormat; import net.filebot.media.VideoFormat;
import net.filebot.mediainfo.MediaInfo; import net.filebot.mediainfo.MediaInfo;
import net.filebot.mediainfo.MediaInfo.StreamKind; import net.filebot.mediainfo.MediaInfo.StreamKind;
@ -201,7 +201,7 @@ public class MediaBindingBean {
} }
// enforce title length limit by default // enforce title length limit by default
return truncateText(t, PlexNamingStandard.TITLE_MAX_LENGTH); return truncateText(t, NamingStandard.TITLE_MAX_LENGTH);
} }
@Define("d") @Define("d")
@ -1074,7 +1074,18 @@ public class MediaBindingBean {
@Define("plex") @Define("plex")
public File getPlexStandardPath() throws Exception { public File getPlexStandardPath() throws Exception {
String path = new PlexNamingStandard().getPath(infoObject); String path = NamingStandard.PLEX.getPath(infoObject);
try {
path = path.concat(getSubtitleTags()); // NPE if {subt} is undefined
} catch (Exception e) {
// ignore => no language tags
}
return new File(path);
}
@Define("kodi")
public File getKodiStandardPath() throws Exception {
String path = NamingStandard.KODI.getPath(infoObject);
try { try {
path = path.concat(getSubtitleTags()); // NPE if {subt} is undefined path = path.concat(getSubtitleTags()); // NPE if {subt} is undefined
} catch (Exception e) { } catch (Exception e) {

View File

@ -13,8 +13,32 @@ import net.filebot.web.Episode;
import net.filebot.web.EpisodeFormat; import net.filebot.web.EpisodeFormat;
import net.filebot.web.Movie; import net.filebot.web.Movie;
import net.filebot.web.MoviePart; import net.filebot.web.MoviePart;
import net.filebot.web.MultiEpisode;
public class PlexNamingStandard { public class NamingStandard {
/**
* Plex Naming Standard (i.e. Default Implementation)
*
* https://support.plex.tv/articles/categories/media-preparation/
*/
public static final NamingStandard PLEX = new NamingStandard();
/**
* Kodi Naming Standard (i.e. same as Plex, but with SxE and non-range based multi-episode formatting)
*
* https://kodi.wiki/view/Naming_video_files
*/
public static final NamingStandard KODI = new NamingStandard() {
@Override
public String formatEpisodeNumbers(Episode e) {
if (e instanceof MultiEpisode) {
return EpisodeFormat.SeasonEpisode.formatMultiSxE((MultiEpisode) e);
}
return EpisodeFormat.SeasonEpisode.formatSxE(e);
}
};
public String getPath(Object o) { public String getPath(Object o) {
if (o instanceof Episode) if (o instanceof Episode)
@ -133,7 +157,6 @@ public class PlexNamingStandard {
// TV Series // TV Series
return EpisodeFormat.SeasonEpisode.formatS00E00(e); return EpisodeFormat.SeasonEpisode.formatS00E00(e);
} }
} }
public String formatEpisodeTitle(Episode e) { public String formatEpisodeTitle(Episode e) {

View File

@ -2,4 +2,4 @@
parameter.exclude: ^StreamKind|^UniqueID|^StreamOrder|^ID|Count$ parameter.exclude: ^StreamKind|^UniqueID|^StreamOrder|^ID|Count$
# preview expressions (keys are tagged so they can be sorted alphabetically) # preview expressions (keys are tagged so they can be sorted alphabetically)
expressions: n, y, s, e, sxe, s00e00, t, d, startdate, absolute, ny, es, sy, sc, di, dc, age, special, episode, series, primaryTitle, alias, movie, tmdbid, imdbid, pi, pn, lang, subt, plex, az, type, anime, regular, music, album, artist, albumArtist, actors, director, collection, genre, genres, languages, runtime, certification, rating, votes, vc, ac, cf, vf, hpi, aco, af, channels, resolution, dim, width, height, bitdepth, hdr, bitrate, kbps, mbps, khz, ws, hd, source, tags, s3d, group, original, info, info.network, info.status, info.productionCompanies, info.productionCountries, info.certifications, info.certifications.AU, info.certifications.DE, omdb.rating, omdb.votes, localize.deu.n, localize.deu.t, localize.zho.n, localize.zho.t, order.airdate.sxe, order.dvd.sxe, fn, ext, mediaType, mediaPath, file, file.name, folder, folder.name, mediaTitle, audioLanguages, textLanguages, duration, seconds, minutes, hours, bytes, megabytes, gigabytes, crc32, media.title, media.collection, media.season, media.part, media.partID, media.genre, media.contentType, media.description, media.lyrics, video[0].codecID, video[0].frameRate, video[0].displayAspectRatioString, video[0].scanType, audio.language, audio[0].bitRateString, audio[0].language, text.language, text[0].language, text[0].codecInfo, camera, camera.maker, camera.model, location, location.country expressions: n, y, s, e, sxe, s00e00, t, d, startdate, absolute, ny, es, sy, sc, di, dc, age, special, episode, series, primaryTitle, alias, movie, tmdbid, imdbid, pi, pn, lang, subt, plex, plex.name, kodi, kodi.name, az, type, anime, regular, music, album, artist, albumArtist, actors, director, collection, genre, genres, languages, runtime, certification, rating, votes, vc, ac, cf, vf, hpi, aco, af, channels, resolution, dim, width, height, bitdepth, hdr, bitrate, kbps, mbps, khz, ws, hd, source, tags, s3d, group, original, info, info.network, info.status, info.productionCompanies, info.productionCountries, info.certifications, info.certifications.AU, info.certifications.DE, omdb.rating, omdb.votes, localize.deu.n, localize.deu.t, localize.zho.n, localize.zho.t, order.airdate.sxe, order.dvd.sxe, fn, ext, mediaType, mediaPath, file, file.name, folder, folder.name, mediaTitle, audioLanguages, textLanguages, duration, seconds, minutes, hours, bytes, megabytes, gigabytes, crc32, media.title, media.collection, media.season, media.part, media.partID, media.genre, media.contentType, media.description, media.lyrics, video[0].codecID, video[0].frameRate, video[0].displayAspectRatioString, video[0].scanType, audio.language, audio[0].bitRateString, audio[0].language, text.language, text[0].language, text[0].codecInfo, camera, camera.maker, camera.model, location, location.country

View File

@ -119,6 +119,21 @@ public class EpisodeFormat extends Format {
}).collect(joining(" - ")); }).collect(joining(" - "));
} }
public String formatMultiSxE(Iterable<Episode> episodes) {
return formatMultiNumbers(episodes, "%01dx", "%02d", "x");
}
public String formatMultiS00E00(Iterable<Episode> episodes) {
return formatMultiNumbers(episodes, "S%02d", "E%02d", "-");
}
public String formatMultiNumbers(Iterable<Episode> episodes, String seasonFormat, String episodeFormat, String delimiter) {
return getSeasonEpisodeNumbers(episodes).entrySet().stream().map(it -> {
String s = it.getKey() >= 0 ? String.format(seasonFormat, it.getKey()) : "";
return it.getValue().stream().distinct().map(i -> String.format(episodeFormat, i)).collect(joining(delimiter, s, ""));
}).collect(joining(" - "));
}
private SortedMap<Integer, SortedSet<Integer>> getSeasonEpisodeNumbers(Iterable<Episode> episodes) { private SortedMap<Integer, SortedSet<Integer>> getSeasonEpisodeNumbers(Iterable<Episode> episodes) {
SortedMap<Integer, SortedSet<Integer>> n = new TreeMap<Integer, SortedSet<Integer>>(); SortedMap<Integer, SortedSet<Integer>> n = new TreeMap<Integer, SortedSet<Integer>>();
for (Episode it : episodes) { for (Episode it : episodes) {

View File

@ -5,9 +5,10 @@ import static java.util.Collections.*;
import static java.util.stream.Collectors.*; import static java.util.stream.Collectors.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator;
import java.util.List; import java.util.List;
public class MultiEpisode extends Episode { public class MultiEpisode extends Episode implements Iterable<Episode> {
protected Episode[] episodes; protected Episode[] episodes;
@ -23,6 +24,11 @@ public class MultiEpisode extends Episode {
this.episodes = episodes.toArray(new Episode[0]); this.episodes = episodes.toArray(new Episode[0]);
} }
@Override
public Iterator<Episode> iterator() {
return stream(episodes).iterator();
}
public List<Episode> getEpisodes() { public List<Episode> getEpisodes() {
return unmodifiableList(asList(episodes)); return unmodifiableList(asList(episodes));
} }