Showing 1 changed files with 58 additions and 0 deletions
+58 -0
media-importer.sh
@@ -752,6 +752,64 @@ extract_file_date() {
752 752
     local date_source=""
753 753
     local exif_found=0
754 754
 
755
+    # Timecode of first frame is the most reliable local-time source for video files:
756
+    # it is written by the camera's own clock at the moment recording starts, with no
757
+    # UTC-vs-local ambiguity. Try it first (requires mediainfo) before any other method.
758
+    if [[ "$DATE_SOURCE" != "filesystem" ]] && command -v mediainfo &>/dev/null; then
759
+        local raw_tc
760
+        raw_tc=$(mediainfo --Inform="Other;%TimeCode_FirstFrame%" "$file" 2>/dev/null | head -1)
761
+        if [[ "$raw_tc" =~ ^([0-9]{2}):([0-9]{2}):([0-9]{2}): ]]; then
762
+            local tc_hh="${BASH_REMATCH[1]}" tc_mm="${BASH_REMATCH[2]}" tc_ss="${BASH_REMATCH[3]}"
763
+            local dt_re='^([0-9]{4}-[0-9]{2}-[0-9]{2})[T ]([0-9]{2}):[0-9]{2}:[0-9]{2}'
764
+            local ref_date="" ref_hh=""
765
+
766
+            # Primary: Recorded_Date includes timezone offset so its date+hour are already local.
767
+            local recorded
768
+            recorded=$(mediainfo --Inform="General;%Recorded_Date%" "$file" 2>/dev/null | head -1)
769
+            if [[ "$recorded" =~ $dt_re ]]; then
770
+                ref_date="${BASH_REMATCH[1]}"
771
+                ref_hh="${BASH_REMATCH[2]}"
772
+            else
773
+                # Fallback: Encoded_Date is stored as UTC; convert to local to get the correct
774
+                # reference date and hour for midnight-crossing detection.
775
+                local encoded
776
+                encoded=$(mediainfo --Inform="General;%Encoded_Date%" "$file" 2>/dev/null | head -1)
777
+                encoded="${encoded% UTC}"
778
+                if [[ "$encoded" =~ $dt_re ]]; then
779
+                    local enc_date="${BASH_REMATCH[1]}" enc_time_str
780
+                    enc_time_str="${encoded#* }"
781
+                    if [[ "$OSTYPE" == "darwin"* ]]; then
782
+                        local utc_ts local_dt
783
+                        utc_ts=$(TZ=UTC date -j -f "%Y-%m-%d %H:%M:%S" "$encoded" "+%s" 2>/dev/null)
784
+                        local_dt=$(date -j -r "$utc_ts" "+%Y-%m-%d %H" 2>/dev/null)
785
+                        ref_date="${local_dt% *}"
786
+                        ref_hh="${local_dt#* }"
787
+                    else
788
+                        ref_date=$(date -d "$encoded UTC" "+%Y-%m-%d" 2>/dev/null)
789
+                        ref_hh=$(date -d "$encoded UTC" "+%H" 2>/dev/null)
790
+                    fi
791
+                fi
792
+            fi
793
+
794
+            if [[ -n "$ref_date" && -n "$ref_hh" ]]; then
795
+                local start_date="$ref_date"
796
+                # If timecode hour > end-time local hour, the clip crossed midnight:
797
+                # subtract one day to get the recording-start calendar date.
798
+                if (( 10#$tc_hh > 10#$ref_hh )); then
799
+                    if [[ "$OSTYPE" == "darwin"* ]]; then
800
+                        start_date=$(date -j -v-1d -f "%Y-%m-%d" "$ref_date" "+%Y-%m-%d" 2>/dev/null)
801
+                    else
802
+                        start_date=$(date -d "$ref_date - 1 day" "+%Y-%m-%d" 2>/dev/null)
803
+                    fi
804
+                fi
805
+                if [[ -n "$start_date" ]]; then
806
+                    echo "${start_date} ${tc_hh}:${tc_mm}:${tc_ss}|Timecode"
807
+                    return 0
808
+                fi
809
+            fi
810
+        fi
811
+    fi
812
+
755 813
     # Filesystem authoritative mode, and GoPro media in auto mode.
756 814
     # GoPro fallback order is THM, LRV, then the MP4 filesystem timestamp.
757 815
     if [[ "$DATE_SOURCE" == "filesystem" ]] || { [[ "$DATE_SOURCE" == "auto" ]] && should_prefer_gopro_filesystem_date "$file"; }; then