diff options
-rw-r--r-- | gtk/configure.ac | 1 | ||||
-rw-r--r-- | gtk/src/callbacks.c | 85 | ||||
-rw-r--r-- | gtk/src/ghb.m4 | 2 | ||||
-rw-r--r-- | macosx/HBPreferencesController.m | 2 | ||||
-rw-r--r-- | macosx/HBTitle.h | 17 | ||||
-rw-r--r-- | macosx/HBTitle.m | 31 | ||||
-rw-r--r-- | macosx/HBUtilities.h | 3 | ||||
-rw-r--r-- | macosx/HBUtilities.m | 44 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Constants.cs | 14 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Helpers/AutoNameHelper.cs | 38 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Properties/Resources.Designer.cs | 2 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Properties/Resources.resx | 2 |
12 files changed, 225 insertions, 16 deletions
diff --git a/gtk/configure.ac b/gtk/configure.ac index fd196562e..fc2eee0c0 100644 --- a/gtk/configure.ac +++ b/gtk/configure.ac @@ -7,6 +7,7 @@ AM_INIT_AUTOMAKE([1.7.9 foreign dist-bzip2 dist-zip]) AM_CONFIG_HEADER(config.h) AM_MAINTAINER_MODE +AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC(gcc clang) AC_ISC_POSIX AC_PROG_CXX(g++ clang++) diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c index bee41d115..bf00b7a38 100644 --- a/gtk/src/callbacks.c +++ b/gtk/src/callbacks.c @@ -50,6 +50,8 @@ #include <netinet/in.h> #include <netdb.h> +#include <regex.h> + #if !defined(_NO_UPDATE_CHECK) #if defined(_OLD_WEBKIT) #include <webkit.h> @@ -1050,6 +1052,71 @@ check_name_template(signal_user_data_t *ud, const char *str) return FALSE; } +static int +match_by_pattern(const char *string, const char *pattern) +{ + int status; + regex_t re; + if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) + { + return 0; + } + status = regexec(&re, string, (size_t) 0, NULL, 0); + regfree(&re); + if (status != 0) + { + return 0; + } + return 1; +} + +typedef struct { + const char *pattern; + const char *format; +} datemap; + +static int +parse_datestring(const char *src, struct tm *tm) +{ + datemap ymdThmsZ = {"[0-9]{4}-[0-1]?[0-9]-[0-3]?[0-9]T[0-9]{2}:[0-9]{2}:[0-9]{2}Z", "%Y-%m-%dT%H:%M:%SZ"}; + + datemap maps[1] = { ymdThmsZ }; + + for (int i = 0; i < sizeof(maps); i++) + { + if (match_by_pattern(src, maps[i].pattern)) + { + strptime(src, maps[i].format, tm); + return 1; + } + } + return 0; +} + +static char* +get_creation_date(const char *pattern, const char *metaValue, const char *file) +{ + char date[11] = ""; + if (metaValue != NULL && strlen(metaValue) > 1) + { + struct tm tm; + if (parse_datestring(metaValue, &tm)) + { + strftime(date, 11, pattern, &tm); + } + } + else + { + struct stat stbuf; + if (g_stat(file, &stbuf) == 0){ + struct tm *tm; + tm = localtime(&(stbuf.st_mtime)); + strftime(date, 11, pattern, tm); + } + } + return strdup(date); +} + static void set_destination_settings(signal_user_data_t *ud, GhbValue *settings) { @@ -1138,6 +1205,24 @@ set_destination_settings(signal_user_data_t *ud, GhbValue *settings) g_string_append_printf(str, "%s", dt); p += strlen("{date}"); } + else if (!strncmp(p, "{creation-date}", strlen("{creation-date}"))) + { + gchar *val; + const gchar *source = ghb_dict_get_string(ud->globals, "scan_source"); + val = get_creation_date("%Y-%m-%d", ghb_dict_get_string(settings, "MetaReleaseDate"), source); + g_string_append_printf(str, "%s", val); + p += strlen("{creation-date}"); + g_free(val); + } + else if (!strncmp(p, "{creation-time}", strlen("{creation-time}"))) + { + gchar *val; + const gchar *source = ghb_dict_get_string(ud->globals, "scan_source"); + val = get_creation_date("%H:%M", ghb_dict_get_string(settings, "MetaReleaseDate"), source); + g_string_append_printf(str, "%s", val); + p += strlen("{creation-time}"); + g_free(val); + } else if (!strncmp(p, "{quality}", strlen("{quality}"))) { if (ghb_dict_get_bool(settings, "vquality_type_constant")) diff --git a/gtk/src/ghb.m4 b/gtk/src/ghb.m4 index 26a0ed00a..226da35ca 100644 --- a/gtk/src/ghb.m4 +++ b/gtk/src/ghb.m4 @@ -7412,7 +7412,7 @@ filter_output([ </child> <child> <object class="GtkEntry" id="auto_name_template"> - <property name="tooltip_text" translatable="yes">Available Options: {source} {title} {preset} {chapters} {date} {time} {quality} {bitrate}</property> + <property name="tooltip_text" translatable="yes">Available Options: {source} {title} {preset} {chapters} {date} {time} {creation-date} {creation-time} {quality} {bitrate}</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> diff --git a/macosx/HBPreferencesController.m b/macosx/HBPreferencesController.m index aa02821f6..3f66bd717 100644 --- a/macosx/HBPreferencesController.m +++ b/macosx/HBPreferencesController.m @@ -122,7 +122,7 @@ [self.formatTokenField setTokenizingCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@"%%"]]; [self.formatTokenField setCompletionDelay:0.2]; - _buildInFormatTokens = @[@"{Source}", @"{Title}", @"{Date}", @"{Time}", @"{Chapters}", @"{Quality/Bitrate}"]; + _buildInFormatTokens = @[@"{Source}", @"{Title}", @"{Date}", @"{Time}", @"{Creation-Date}", @"{Creation-Time}", @"{Chapters}", @"{Quality/Bitrate}"]; [self.builtInTokenField setTokenizingCharacterSet:[NSCharacterSet characterSetWithCharactersInString:@"%%"]]; [self.builtInTokenField setStringValue:[self.buildInFormatTokens componentsJoinedByString:@"%%"]]; diff --git a/macosx/HBTitle.h b/macosx/HBTitle.h index 79869ea20..3979b6d4c 100644 --- a/macosx/HBTitle.h +++ b/macosx/HBTitle.h @@ -11,6 +11,21 @@ NS_ASSUME_NONNULL_BEGIN @class HBChapter; @class HBPreset; +@interface HBMetadata : NSObject + +@property (nonatomic, readonly, nullable) NSString *name; +@property (nonatomic, readonly, nullable) NSString *artist; +@property (nonatomic, readonly, nullable) NSString *composer; +@property (nonatomic, readonly, nullable) NSString *releaseDate; +@property (nonatomic, readonly, nullable) NSString *comment; +@property (nonatomic, readonly, nullable) NSString *album; +@property (nonatomic, readonly, nullable) NSString *albumArtist; +@property (nonatomic, readonly, nullable) NSString *genre; +@property (nonatomic, readonly, nullable) NSString *description; +@property (nonatomic, readonly, nullable) NSString *longDescription; + +@end + /** * HBTitles is an interface to the low-level hb_title_t. * the properties are lazy-loaded. @@ -46,6 +61,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSArray<NSDictionary<NSString *, id> *> *subtitlesTracks; @property (nonatomic, readonly) NSArray<HBChapter *> *chapters; +@property (nonatomic, readonly) HBMetadata *metadata; + @end NS_ASSUME_NONNULL_END diff --git a/macosx/HBTitle.m b/macosx/HBTitle.m index e718624a7..01cf53bca 100644 --- a/macosx/HBTitle.m +++ b/macosx/HBTitle.m @@ -25,12 +25,37 @@ extern NSString *keySubTrackName; extern NSString *keySubTrackLanguageIsoCode; extern NSString *keySubTrackType; +@interface HBMetadata () + +@property (nonatomic, readonly) hb_metadata_t *hb_metadata; +@property (nonatomic, copy) NSString *releaseDate; + +@end + +@implementation HBMetadata +- (instancetype)initWithMetadata:(hb_metadata_t *)data +{ + _hb_metadata = data; + return self; +} + +- (NSString *)releaseDate +{ + if (self.hb_metadata->release_date == nil) + { + return nil; + } + return @(self.hb_metadata->release_date); +} + +@end + @interface HBTitle () @property (nonatomic, readonly) hb_title_t *hb_title; @property (nonatomic, readonly) hb_handle_t *hb_handle; @property (nonatomic, readwrite, copy) NSString *name; - +@property (nonatomic, readwrite) HBMetadata *metadata; @property (nonatomic, readwrite) NSArray *audioTracks; @property (nonatomic, readwrite) NSArray *subtitlesTracks; @property (nonatomic, readwrite) NSArray *chapters; @@ -52,12 +77,14 @@ extern NSString *keySubTrackType; _hb_title = title; _hb_handle = handle; _featured = featured; + + _metadata = [[HBMetadata alloc] initWithMetadata:title->metadata]; } return self; } -- (NSString *)name +- (NSString *)name { if (!_name) { diff --git a/macosx/HBUtilities.h b/macosx/HBUtilities.h index 4b4fa035a..fd8963693 100644 --- a/macosx/HBUtilities.h +++ b/macosx/HBUtilities.h @@ -58,7 +58,8 @@ NS_ASSUME_NONNULL_BEGIN chapters:(NSRange)chaptersRange quality:(double)quality bitrate:(int)bitrate - videoCodec:(uint32_t)codec; + videoCodec:(uint32_t)codec + creationDate:(NSDate *)creationDate; + (NSString *)isoCodeForNativeLang:(NSString *)language; + (NSString *)iso6392CodeFor:(NSString *)language; diff --git a/macosx/HBUtilities.m b/macosx/HBUtilities.m index 5794a712b..ae2658444 100644 --- a/macosx/HBUtilities.m +++ b/macosx/HBUtilities.m @@ -155,9 +155,29 @@ return mediaURL; } ++ (nullable NSDate *)getReleaseDate:(HBTitle *)title +{ + if ([title.metadata.releaseDate length] == 0){ + return nil; + } + + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + NSString *dt = title.metadata.releaseDate; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"]; + NSDate *releaseDate = [dateFormatter dateFromString:dt]; + return releaseDate; +} + + (NSString *)automaticNameForJob:(HBJob *)job { HBTitle *title = job.title; + + NSDate *releaseDate = [self getReleaseDate:title]; + if (releaseDate == nil) + { + NSDictionary* fileAttribs = [[NSFileManager defaultManager] attributesOfItemAtPath:job.fileURL.path error:nil]; + releaseDate = [fileAttribs objectForKey:NSFileCreationDate]; + } // Generate a new file name NSString *fileName = [HBUtilities automaticNameForSource:title.name @@ -165,7 +185,9 @@ chapters:NSMakeRange(job.range.chapterStart + 1, job.range.chapterStop + 1) quality:job.video.qualityType ? job.video.quality : 0 bitrate:!job.video.qualityType ? job.video.avgBitrate : 0 - videoCodec:job.video.encoder]; + videoCodec:job.video.encoder + creationDate:releaseDate + ]; return fileName; } @@ -206,7 +228,6 @@ // use the correct extension based on the container NSString *ext = [self automaticExtForJob:job]; fileName = [fileName stringByAppendingPathExtension:ext]; - return fileName; } @@ -216,10 +237,12 @@ quality:(double)quality bitrate:(int)bitrate videoCodec:(uint32_t)codec + creationDate:(NSDate *)creationDate { NSMutableString *name = [[NSMutableString alloc] init]; // The format array contains the tokens as NSString NSArray *format = [[NSUserDefaults standardUserDefaults] objectForKey:@"HBAutoNamingFormat"]; + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; for (NSString *formatKey in format) { @@ -250,7 +273,6 @@ { NSDate *date = [NSDate date]; NSString *dateString = nil; - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateStyle:NSDateFormatterShortStyle]; [formatter setTimeStyle:NSDateFormatterNoStyle]; dateString = [[formatter stringFromDate:date] stringByReplacingOccurrencesOfString:@"/" withString:@"-"]; @@ -259,11 +281,25 @@ else if ([formatKey isEqualToString:@"{Time}"]) { NSDate *date = [NSDate date]; - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateStyle:NSDateFormatterNoStyle]; [formatter setTimeStyle:NSDateFormatterShortStyle]; [name appendString:[formatter stringFromDate:date]]; } + else if ([formatKey isEqualToString:@"{Creation-Date}"]) + { + NSString *dateString = nil; + [formatter setDateStyle:NSDateFormatterShortStyle]; + [formatter setTimeStyle:NSDateFormatterNoStyle]; + dateString = [[formatter stringFromDate:creationDate] stringByReplacingOccurrencesOfString:@"/" withString:@"-"]; + [name appendString:dateString]; + + } + else if ([formatKey isEqualToString:@"{Creation-Time}"]) + { + [formatter setDateStyle:NSDateFormatterNoStyle]; + [formatter setTimeStyle:NSDateFormatterShortStyle]; + [name appendString:[formatter stringFromDate:creationDate]]; + } else if ([formatKey isEqualToString:@"{Chapters}"]) { if (chaptersRange.location == chaptersRange.length) diff --git a/win/CS/HandBrakeWPF/Constants.cs b/win/CS/HandBrakeWPF/Constants.cs index cf322e85c..f13d8cb0f 100644 --- a/win/CS/HandBrakeWPF/Constants.cs +++ b/win/CS/HandBrakeWPF/Constants.cs @@ -50,16 +50,26 @@ namespace HandBrakeWPF public const string Quality = "{quality}";
/// <summary>
- /// The quality.
+ /// The creation date of the new output file.
/// </summary>
public const string Date = "{date}";
/// <summary>
- /// The quality.
+ /// The creation time of the new output file.
/// </summary>
public const string Time = "{time}";
/// <summary>
+ /// The source creation date.
+ /// </summary>
+ public const string CretaionDate = "{creation-date}";
+
+ /// <summary>
+ /// The source creation time.
+ /// </summary>
+ public const string CreationTime = "{creation-time}";
+
+ /// <summary>
/// The bitrate.
/// </summary>
public const string Bitrate = "{bitrate}";
diff --git a/win/CS/HandBrakeWPF/Helpers/AutoNameHelper.cs b/win/CS/HandBrakeWPF/Helpers/AutoNameHelper.cs index 4ef7405f2..4fd7ec1fc 100644 --- a/win/CS/HandBrakeWPF/Helpers/AutoNameHelper.cs +++ b/win/CS/HandBrakeWPF/Helpers/AutoNameHelper.cs @@ -18,6 +18,7 @@ namespace HandBrakeWPF.Helpers using HandBrake.Interop.Interop.Model.Encoding;
using HandBrakeWPF.Extensions;
+ using HandBrakeWPF.Services.Encode.Model;
using HandBrakeWPF.Services.Interfaces;
using HandBrakeWPF.Services.Presets.Model;
@@ -86,6 +87,35 @@ namespace HandBrakeWPF.Helpers if (chapterFinish != chapterStart && chapterFinish != string.Empty)
combinedChapterTag = chapterStart + "-" + chapterFinish;
+ // Local method to check if we have a creation time in the meta data. If not, fall back to source file creation time.
+ DateTime obtainCreateDateObject()
+ {
+ var rd = task.MetaData.ReleaseDate;
+ if (DateTime.TryParse(rd, out var d))
+ {
+ return d;
+ }
+ try
+ {
+ return File.GetCreationTime(task.Source);
+ }
+ catch (Exception e)
+ {
+ if (e is UnauthorizedAccessException ||
+ e is PathTooLongException ||
+ e is NotSupportedException)
+ {
+ // Suspect the most likely concerns trying to grab the creation date in which we would want to swallow exception.
+ return default(DateTime);
+ }
+ throw e;
+ }
+ }
+
+ var creationDateTime = obtainCreateDateObject();
+ string createDate = creationDateTime.Date.ToShortDateString().Replace('/', '-');
+ string createTime = creationDateTime.ToString("HH-mm");
+
/*
* File Name
*/
@@ -98,7 +128,9 @@ namespace HandBrakeWPF.Helpers .Replace(Constants.Title, dvdTitle)
.Replace(Constants.Chapters, combinedChapterTag)
.Replace(Constants.Date, DateTime.Now.Date.ToShortDateString().Replace('/', '-'))
- .Replace(Constants.Time, DateTime.Now.ToString("HH:mm"));
+ .Replace(Constants.Time, DateTime.Now.ToString("HH-mm"))
+ .Replace(Constants.CretaionDate, createDate)
+ .Replace(Constants.CreationTime, createTime);
// .Replace(Constants.Preset, sanitisedPresetName);
if (task.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality)
@@ -199,7 +231,7 @@ namespace HandBrakeWPF.Helpers return autoNamePath;
}
-
+
/// <summary>
/// Check if there is a valid autoname path.
/// </summary>
@@ -219,6 +251,6 @@ namespace HandBrakeWPF.Helpers return userSettingService.GetUserSetting<string>(UserSettingConstants.AutoNamePath).Trim().StartsWith("{source_path}") ||
(userSettingService.GetUserSetting<string>(UserSettingConstants.AutoNamePath).Contains("{source_folder_name}") ||
Directory.Exists(userSettingService.GetUserSetting<string>(UserSettingConstants.AutoNamePath).Trim()));
- }
+ }
}
}
diff --git a/win/CS/HandBrakeWPF/Properties/Resources.Designer.cs b/win/CS/HandBrakeWPF/Properties/Resources.Designer.cs index bbe866be0..d5ccb157f 100644 --- a/win/CS/HandBrakeWPF/Properties/Resources.Designer.cs +++ b/win/CS/HandBrakeWPF/Properties/Resources.Designer.cs @@ -1242,7 +1242,7 @@ namespace HandBrakeWPF.Properties { /// Looks up a localized string similar to The format of the output file. In addition to any supported file system character, you can use the following placeholders that will be replaced when you change title or scan a source.
///
///Live Update Options: {source} {title} {chapters}
- ///Non-Live Options: {date} {time} {quality} {bitrate} (These only change if you scan a new source, change title or chapters).
+ ///Non-Live Options: {date} {time} {creation-date} {creation-time} {quality} {bitrate} (These only change if you scan a new source, change title or chapters).
/// </summary>
public static string Options_AdditionalFormatOptions {
get {
diff --git a/win/CS/HandBrakeWPF/Properties/Resources.resx b/win/CS/HandBrakeWPF/Properties/Resources.resx index 21bed46f7..1c0e4c1f2 100644 --- a/win/CS/HandBrakeWPF/Properties/Resources.resx +++ b/win/CS/HandBrakeWPF/Properties/Resources.resx @@ -479,7 +479,7 @@ Do you wish to proceed?</value> <value>The format of the output file. In addition to any supported file system character, you can use the following placeholders that will be replaced when you change title or scan a source.
Live Update Options: {source} {title} {chapters}
-Non-Live Options: {date} {time} {quality} {bitrate} (These only change if you scan a new source, change title or chapters)</value>
+Non-Live Options: {date} {time} {creation-date} {creation-time} {quality} {bitrate} (These only change if you scan a new source, change title or chapters)</value>
</data>
<data name="Options_DefaultPathAdditionalParams" xml:space="preserve">
<value>Available additional Options: {source_path} or {source_folder_name}
|