Jump to content

failu menedždzeris


constrig
 Share

Recommended Posts

Sveiki!

Vai ir kāda bezmaksas utīla priekš Win, ar kuras palīdzību iespējams sameklēt, nofiltrēt foto (no visiem failiem kas uz diska) definējot konkrētu datumu, datu avotam jābūt  no exif (create date) datiem.

 

Līdzīgi, kā Onedrive (Memory Pictures) to dara. 

Paldies

Link to comment
Share on other sites

Kādreiz vai tikai Irfanview kaut ko tādu nemācēja, plus tam ir kaudze pluginu, gan jau kāds māk to ko vajag.

Link to comment
Share on other sites

(labots)

Ehh, zirgā sēž un zirgu meklē :)

Paver File explorer un search laukā iesit DateTaken:20.09.2023 vai kādu tur vajag.

Vienīgā nianse, ka datorā var atšķirties reģionālie settingi, tāpēc jāpārslēdz explorera skatu Details režīmā (view poga) un datuma/laika formātu var pašpikot no Date modified lauka.

Vēl Details skatā var uzklikšķināt uz aiļu galvenes (column header) ar žurkas labo pogu un pielikt Date created lauku (spied pa priekšu More), pēc kura varēsi šķirot.

Rezultāts abām darbībām aptuveni šāds:

image.thumb.png.6ae3f1f87992823dbf0406c79e60cb6e.png

Labots - Boņs
  • Patīk 2
Link to comment
Share on other sites

constrig

Boņs variants neder, jo tie nebūs EXIF dati (faktiskais bildēšanas datums atšķiras no faila izveides datuma)

Link to comment
Share on other sites

AndrisBB
(labots)

Var jau pielikt 'Date Taken' kolonu un paskatīties vai tas sakrīt ar 'Date Created'.

Kas to zin ko Windows File Explorers dara, bet uz Mac parasti kad kopē failus no kameras/telefona, tad viņš ieliek 'Date Created' nevis, kad fails tika pārkopēts, bet izvelk no EXIF.

 

MWFwm.png

 

aRLkJ.png

Labots - AndrisBB
Link to comment
Share on other sites

binary
1 stundu atpakaļ, constrig teica:

Boņs variants neder, jo tie nebūs EXIF dati (faktiskais bildēšanas datums atšķiras no faila izveides datuma)

Kas tad tie būs, ja ne EXIF dati?

  • Patīk 1
Link to comment
Share on other sites

HIGH-Zen
pirms 2 stundām , constrig teica:

Boņs variants neder

Der, der (Windows 11):

Untitled.thumb.png.fbb4c214b0e4cb19e92f47903960ef4b.png

 

 

 

 

Link to comment
Share on other sites

constrig

Jā, sory, taisnība.

LV win variantā meklēšanas laukā jāraksta  "uzņemšanasdatums:16/02/2002" 

bet man nepieciešams konkrēta datuma uzņemtās bildes ne tikai vienā 2002 gadā, bet no 2002. līdz 2024. gadam.

pirms 18 stundām , HIGH-Zen teica:

Ja citi varianti neder, tad var uzrakstīt Python skriptu.

kādas izmaksas izdarīt augstāk minēto, pretstatā gatavam produktam?

image.png.6b6343de0560f77a2c783e5b10d5386b.png

Link to comment
Share on other sites

HIGH-Zen
(labots)
1 stundu atpakaļ, constrig teica:

kādas izmaksas izdarīt augstāk minēto, pretstatā gatavam produktam?

Viena pēcpusdiena. Uz mani neskaties, man nav laika, tagad mācos LISP.

 

Vēl vienkāršāks veids ar NO, LĪDZ datumiem var meklēt iekš Double Commander.

Alt+F7 -> File mask = *.jpg -> Advanced -> Date form, Date to.

Feed to Listbox.

Labots - HIGH-Zen
Link to comment
Share on other sites

binary
(labots)
Pirms 16 minūtēm , HIGH-Zen teica:

Vēl vienkāršāks veids ar NO, LĪDZ datumiem var meklēt iekš Double Commander.

Viņš prot EXIF?

 

1 stundu atpakaļ, constrig teica:

bet man nepieciešams konkrēta datuma uzņemtās bildes ne tikai vienā 2002 gadā, bet no 2002. līdz 2024. gadam.

Katra gada konkrētā datumā? "*.jpg datetaken:*-01-01 datetaken:>=2002" (vai kāds nu tev tur ir tas formāts) nestrādā?

Labots - binary
Link to comment
Share on other sites

HIGH-Zen
(labots)
Pirms 50 minūtēm , binary teica:

Viņš prot EXIF?

Man (uz Windows 11) .jpg failiem Date laukā rāda datumu no EXIF. Notestēju uz sava datora, viss darbojas.

 

Edit:

Uzbraucot ar peli virs bildes tā arī rāda - Date taken.

 

Edit2:

Patestēju vēl.

Iekš Double Commander Date laukā rāda - Date Modified.

Ja bilde nav labota kopš uzņemšanas, tad "Date Modified" sakrīt ar "Date taken".

 

Windows Explorer jebkurā gadījumā rāda "Date Taken".

Atlasīt Windows Explorer-ī no - līdz var ar šādu sintaksi: DateTaken:14.01.2021 .. 05.05.2023

 

Labots - HIGH-Zen
Link to comment
Share on other sites

kurtka
(labots)

Runājot par python skriptiem, uzreiz nāca prātā ChatGPT.

Par vai nepar- brīnumu šis iedeva kodu, kas praktiski darbojās out-of-the-box, vajadzēja tikai datuma formātu izlabot.

Nedarbojas, ja beigu datums sakrīt ar sākuma datumu, bet to visu var tūnēt.

Šajā redakcijā izmanto exif 'data-taken' info. Pirmo redakciju šis izveidoja ar faila 'data created' info.

Apm 15 min mana laika. Paldies par iemeslu paprokrastinēt pie man neaktuālas problēmas, tikai lai pārbaudītu savu hipotēzi :D

 

Darbinu ar VSCode:

import os
import shutil
from datetime import datetime
from PIL import Image

def get_exif_date_taken(image_path):
    try:
        with Image.open(image_path) as img:
            exif_data = img._getexif()
            if exif_data:
                date_taken = exif_data.get(36867)  # 36867 corresponds to the 'DateTimeOriginal' tag
                if date_taken:
                    return datetime.strptime(date_taken, "%Y:%m:%d %H:%M:%S")
    except (AttributeError, KeyError, IndexError):
        pass
    return None

def filter_and_copy_photos(source_folder, destination_folder, start_date, end_date):
    # Convert start_date and end_date strings to datetime objects
    start_date = datetime.strptime(start_date, "%d.%m.%Y")
    end_date = datetime.strptime(end_date, "%d.%m.%Y")

    # Create destination folder if it doesn't exist
    if not os.path.exists(destination_folder):
        os.makestāsti(destination_folder)

    # Initialize the count of copied photos
    copied_photos_count = 0

    # Iterate through files in the source folder
    for filename in os.listdir(source_folder):
        file_path = os.path.join(source_folder, filename)

        # Check if the file is a photo (you can customize this check based on your file types)
        if os.path.isfile(file_path) and any(file_path.lower().endswith(ext) for ext in ['.jpg', '.jpeg', '.png']):
            # Get the date taken from EXIF data
            date_taken = get_exif_date_taken(file_path)

            # Check if the file has EXIF data and if the date is within the specified range
            if date_taken and start_date <= date_taken <= end_date:
                # Copy the file to the destination folder
                shutil.copy2(file_path, os.path.join(destination_folder, filename))
                copied_photos_count += 1

    # Print the number of copied photos
    print(f"Number of photos copied: {copied_photos_count}")

# Example usage:
source_folder = "d:/##__FOTO__##/zz_Test_photos/"
destination_folder = "d:/##__FOTO__##/zz_Test_photos_filtered/"
start_date = "04.04.2022"
end_date = "05.04.2022"

filter_and_copy_photos(source_folder, destination_folder, start_date, end_date)

 

Labots - kurtka
  • Patīk 3
Link to comment
Share on other sites

AndrisBB
Pirms 11 minūtēm , kurtka teica:
os.makestāsti(destination_folder)

Pat kodā forums neļauj lamāties 😂

  • Haha 3
Link to comment
Share on other sites

versatile

Jēdzīgāk ir tiešām kāds foto meneģeris, kas veido savu db ar metadatiem. Uz sitienu nepateikšu, šķiet, xnview vai faststone kko tādu prata, bet neesmu drošs.

Link to comment
Share on other sites

kurtka
(labots)
Pirms 11 minūtēm , AndrisBB teica:

Pat kodā forums neļauj lamāties 😂

Un neļauj labot!

 

Foruma cenzūras tests:

makestāsti='makestāsti'?

'maked.i.r.s'='maked.i.r.s'

 

yap. Ja kāds vispār tik tālu tiek: 25. rindā 'os.makestāsti' vietā ir jābūt 'os.maked.i.r.s' bez punktiem. Fenomenāla foruma cenzūra :D, paldies @AndrisBB

Labots - kurtka
Link to comment
Share on other sites

binary
(labots)
pirms 1 stundas , HIGH-Zen teica:

Ja bilde nav labota kopš uzņemšanas, tad "Date Modified" sakrīt ar "Date taken".

Tas atkarīgs no aparatūras.

No Canon fotoaparāta kopēju bildes, liekot SD karti datorā - tad sakrita.

No Sony kopēju caur kabeli, jo tur īpatnēja folderu struktūra uz kartes. Tam, šķiet, faila "izveides" laiku rāda, kad fails ir kopēts no fotoaparāta. Neesmu vēl saņēmies uzrakstīt python skriptu, kas faila laikus no ienādotu ar tiem exif datiem...

Labots - binary
Link to comment
Share on other sites

Xnview MP ir iespēja Show Files in Subfolders, Sort by EXIF date taken, tālāk jau vieglāk meklēt

Link to comment
Share on other sites

raiviic
(labots)
pirms 2 stundām , binary teica:

Tam, šķiet, faila "izveides" laiku rāda, kad fails ir kopēts no fotoaparāta

Gan Sony, gan Canon tā rāda General sadaļā, pie tam vēlāk bildi kaut kur pārkopējot jebkurai bildei mainās , bet zem Details viss saglabājas pareizi.

1.thumb.jpg.b7ce8995b6b50eb1ddb29754de473a42.jpg

 

 

Labots - raiviic
Link to comment
Share on other sites

binary
(labots)

@raiviic, es par to spriedu pēc tā, ka Total Commander "mass rename" fīča no viena aparāta ņemtajām bildēm mācēja "pareizo" datumu izvilkt, no cita aparāta nē. Tā fīča izmanto faila "metadatus" (tos, kas glabājas failu sistēmā), nevis exif (glabājas pašā failā). Kurš tieši no tiem laikiem tur saglabājās "pareizais" (created, modified vai kāds cits) - nav ne jausmas. Zinu tik, ka, atkarībā no aparatūras, tie datumi vai nu atbilst fotografēšanas laikam, vai arī "izvilkšanas no afotoaparāta" laikam.

Labots - binary
Link to comment
Share on other sites

AndrisBB
(labots)
pirms 2 stundām , binary teica:

No Canon fotoaparāta kopēju bildes, liekot SD karti datorā - tad sakrita.

No Sony kopēju caur kabeli, jo tur īpatnēja folderu struktūra uz kartes. Tam, šķiet, faila "izveides" laiku rāda, kad fails ir kopēts no fotoaparāta.

Tas tākā loģiski.

 

Pirmajā gadījumā fotoaparāts uztaisija failu uz SD kartes, ar reālu failu sistēmu un attribūtiem. DateCreated un dati iekš Exif +- sakritīs (kad fotoaparāts saglabāja failu uz SD kartes), ja nu vienīgi kāda milisekunde atšķirība. Pārkopējot uz PC no kartes, izmantojas tas pats DateCreated.

 

Otrajā gadījumā kopējot caur kabeli, nav gluži kopēšana no vienas FS uz citu (kautvai kautkādā softā izskatās ka ir), fails tiek izveidots pa jaunam. Tapēc arī DateCreated ir, tad kad pārkopē. Iespējams ka protokols ko Sony izmanto 'caur vadu' nemaz neatbalsta kautkādus 'papildus' failu metadatus, aka datumus utt, tikai izmēru un nosaukumu. Tapēc arī PC izmanto datumu, kad tika pārkopēts.

Labots - AndrisBB
  • Patīk 1
Link to comment
Share on other sites

AndrisBB
(labots)

Plus iekš Windows ir arī atšķirība starp copy/paste un cut/paste

(vienā gadījumā datums mainīsies, otrā saglabāsies)

Labots - AndrisBB
Link to comment
Share on other sites


Atbildod uz sākotnējo jautājumu, lai varētu redzēt bildes no konkrētā mēneša un dienas, bet visus gadus, man ir tikai viens, diezgan čakarīgs variants. Noskanē folderi, kur ir visas bildes ar exiftool. Komanda manā gadījumā "exiftool -DateTimeOriginal -n -r -csv -progress "C:\Users\inorm\JOB_no_cld\ni\Pictures\- Apsekosana -" > mbildes.csv". Tālāk imports excelī. Excelī izveido papildus kolonnas Mēnesis un otra kolonna Diena. Tālāk filtrs pa vajadzīgo mēneša dienu. Tad nokopē filelist, iekopē, piemēram, txt. Importē ar xnview. Otrs variants atver txt ar jpgview, tik tad / jāpārvēš uz \.image.thumb.png.c09a53228b1fca67ab8f999249b1c18f.png

Link to comment
Share on other sites

TotalCommander + EXIF addons netiek apsvērts? TotalCommander var uzlikt arī triāli. EXIF addons ļauj izskatā ielikt kolonnu, kurā attēlo faila exif datus.

Link to comment
Share on other sites

Pirms šo rakstīju, pamocīju to TC ar eif addoniem. Bet nē, nekas tur nesanāk. Augstākais ko var izveidot, arī Mēneša un diena kolonnas, bet tās nesortējas un nefiltrējas.

Link to comment
Share on other sites

man patīk šie interesantie laiki, kad dažkārt ātrāk un saprotamāk  ir ar AI palīdzību uzrakstīt python vai citu skriptu, kā izburties cauri win search sintaksei vai pārējo softu specifikai. 

Link to comment
Share on other sites

56 minutes ago, nrs said:

Bet nē, nekas tur nesanāk.

image.png

image.png

Rāda OK, sakrīt ar EXIF Date Taken, arī kārtojas OK. 

Link to comment
Share on other sites

Un kā tālāk atlasīsi 22. februāra bildes arī no 2004, 2007, 2010 etc gadiem, kā contric ir vajadzīgs?

 

Link to comment
Share on other sites

Zinu ļoti labi šo find tūli TC. Tik tas atrod konkrēta gada.men.dienas bildi. Bet Atrodi visus gadus 22.februāri, piemēram? Neizdosies.

Link to comment
Share on other sites

binary

Wildcards neprot? T.i., gada vietā "*"?

Link to comment
Share on other sites

HIGH-Zen
(labots)

Augstāk minētais ChatGPT python kods lasa bildes no vienas mapes. Ja vajag rekursīvi visām apakšmapēm, tad tāds nederēs.

 

Edit:

Vispār būtu interesanti uzzināt autora domas - kuru pieeju galu galā viņš izvēlējās.

Labots - HIGH-Zen
Link to comment
Share on other sites

AndrisBB
Pirms 21 minūtēm , HIGH-Zen teica:

Augstāk minētais ChatGPT python kods lasa bildes no vienas mapes. Ja vajag rekursīvi visām apakšmapēm, tad tāds nederēs.

 

Vaitad grūti uzprasīt tam pašam ChatGPT lai iekļauj arī apakšmapes?

Link to comment
Share on other sites

(labots)

papildināju ar apakšmapēm un brutālu tkinter interface. datumu ievadīju tajos logos dd.mm.yyyy darbojās, bet beigās nokārās, nebija laika pētīt kādēļ, minu, ka uzrāvās uz dropbox failu kurš tiek lejupielādēts pēc pieprasījuma, bet explorer izskatās, ka ir. ar šiem python skripti nesadarbojas labi.
 

edit: zemāk aktuālāks

Labots - Usins
Link to comment
Share on other sites

AndrisBB

Te jau gandrīz vai Git repo jātaisa, lai 'interesējošie' var papildināt funkcionalitāti 😂

Link to comment
Share on other sites

Papildus kopēšanai varētu pievienot iespēju veidot shortcutus vai symbolic linkus (hard linkus), vai var papildināt metadatus ar kautkādiem keywords un tad jau var izmantot piemēram XnViewMP meklēšanai pēc keywordiem ...

Link to comment
Share on other sites

(labots)

ekhm, tagad ignorē failus, kas nav lokāli diskā, bet dropbox mākonī, kā arī failus kuriem exif tukšs. 
pievienoju opciju meklēt jebkādus failus. 
iespēja meklēt 16. feb katru gadu darbojas glīti.
'stop' poga bija izaicinājums, darbojas. 
mēģināju noexportēt kā .exe priekš normāliem cilvēkiem, vēl nesāca.
visu rakstīja GPT4, tik cik viņu bakstīju. kas viņam tipiski - neoptimālas diska operācijas, rakstīt log uz katru failu.
visdrīzāk pie šī vēl piestrādāšu (kādreiz, varbūt), jo ļoti riebjas windows meklēšana. 
 

import os
import shutil
from datetime import datetime
import tkinter as tk
from tkinter import filedialog, Label, Button, Entry, Checkbutton, IntVar, ttk  # Import ttk for combobox
from tkcalendar import DateEntry
from PIL import Image
import threading

# Shared variable to control the process
stop_requested = False


def get_exif_date_taken(image_path):
    try:
        with Image.open(image_path) as img:
            exif_data = img._getexif()
            if exif_data:
                date_taken = exif_data.get(36867)  # 36867 corresponds to the 'DateTimeOriginal' tag
                if date_taken:
                    return datetime.strptime(date_taken, "%Y:%m:%d %H:%M:%S")
    except (AttributeError, KeyError, IndexError):
        pass
    return None

def filter_and_copy_photos(source_folder, destination_folder, start_date, end_date, include_subfolders, this_period_every_year, date_type, file_types):
    global stop_requested
    # Reset stop_requested in case it was set in a previous run
    stop_requested = False

    start_date = datetime.strptime(start_date, "%d.%m.%Y")
    end_date = datetime.strptime(end_date, "%d.%m.%Y")

    if not os.path.exists(destination_folder):
        os.makestāsti(destination_folder)

    copied_photos_count = 0
    skipped_files = []

    if include_subfolders:
        for root, stāsti, files in os.walk(source_folder):
            for filename in files:
                if stop_requested: break
                file_path = os.path.join(root, filename)
                # Now correctly passing file_types to process_file
                process_file(file_path, start_date, end_date, destination_folder, copied_photos_count, skipped_files, this_period_every_year, date_type, file_types)
    else:
        for filename in os.listdir(source_folder):
            if stop_requested: break
            file_path = os.path.join(source_folder, filename)
            # Again, make sure to pass file_types to process_file
            process_file(file_path, start_date, end_date, destination_folder, copied_photos_count, skipped_files, this_period_every_year, date_type, file_types)

    log_skipped_files(skipped_files)

    print(f"Number of photos copied: {copied_photos_count}")
    print(f"Number of files skipped: {len(skipped_files)}")


def process_file(file_path, start_date, end_date, destination_folder, copied_photos_count, skipped_files, this_period_every_year, date_type, file_types):
    try:
        # Split the custom file types string into a list
        file_extensions = file_types.split(',')

        if os.path.isfile(file_path) and any(file_path.lower().endswith(ext) for ext in file_extensions):
            if not os.path.getsize(file_path) > 0:
                raise IOError("File is online-only or empty")

            date_taken = get_file_date(file_path, date_type)
            if date_taken and is_date_in_range(date_taken, start_date, end_date, this_period_every_year):
                shutil.copy2(file_path, os.path.join(destination_folder, os.path.basename(file_path)))
                copied_photos_count += 1
            else:
                raise ValueError("Missing or invalid date data")
    except (IOError, ValueError) as e:
        skipped_files.append(file_path)
        print(f"Skipped file (Reason - {e}): {file_path}")

def log_skipped_files(skipped_files):
    # Check if logging is enabled
    if log_skipped_var.get() == 1:
        with open("skipped_files_log.txt", "w") as log_file:
            for file_path in skipped_files:
                log_file.write(f"{file_path}\n")


def select_folder(label):
    folder_selected = filedialog.askdirectory()
    label.config(text=folder_selected)
    return folder_selected

def threaded_filter_and_copy():
    start_date_str = start_date_entry.get_date().strftime('%d.%m.%Y')
    end_date_str = end_date_entry.get_date().strftime('%d.%m.%Y')
    filter_and_copy_photos(source_label['text'], destination_label['text'], start_date_entry.get(), end_date_entry.get(), subfolders_var.get(), this_period_var.get(), date_option_var.get(), file_types_entry.get())

def run_filter():
    # Starting the filter process in a separate thread
    threading.Thread(target=threaded_filter_and_copy).start()
    
def is_date_in_range(date_taken, start_date, end_date, this_period_every_year):
    if this_period_every_year:
        # Adjust the year of the date taken to match the start date for comparison
        date_taken_adjusted = date_taken.replace(year=start_date.year)
        return start_date <= date_taken_adjusted <= end_date
    else:
        return start_date <= date_taken <= end_date

def get_file_date(file_path, date_type):
    if date_type == 'date_modified':
        return datetime.fromtimestamp(os.path.getmtime(file_path))
    elif date_type == 'date_created':
        return datetime.fromtimestamp(os.path.getctime(file_path))
    elif date_type == 'exif_date_taken':
        return get_exif_date_taken(file_path)
    else:
        return None
    
def stop_process():
    global stop_requested
    stop_requested = True


root = tk.Tk()
root.title("File search by date and copy")

source_label = Label(root, text="Select source folder")
source_label.pack()

Button(root, text="Browse", command=lambda: select_folder(source_label)).pack()

destination_label = Label(root, text="Select destination folder")
destination_label.pack()

Button(root, text="Browse", command=lambda: select_folder(destination_label)).pack()

start_date_label = tk.Label(root, text="Date from (included):")
start_date_label.pack()
start_date_entry = DateEntry(root, date_pattern='dd.mm.yyyy')
start_date_entry.pack()

end_date_label = tk.Label(root, text="Date to (NOT included):")
end_date_label.pack()
end_date_entry = DateEntry(root, date_pattern='dd.mm.yyyy')
end_date_entry.pack()


subfolders_var = IntVar()
Checkbutton(root, text="Include subfolders", variable=subfolders_var).pack()

this_period_var = IntVar()
Checkbutton(root, text="This period every year", variable=this_period_var).pack()

# Define the options for the dropdown menu
date_options = ['date_modified', 'date_created', 'exif_date_taken']
date_option_var = tk.StringVar()
date_option_dropdown = ttk.Combobox(root, textvariable=date_option_var, values=date_options)
date_option_dropdown.current(0)  # Default to the first option ('date_modified')
date_option_dropdown.pack()

# Add label for file types entry
file_types_label = tk.Label(root, text="Enter file types, comma separated (e.g., jpg,jpeg,png):")
file_types_label.pack()

# Add entry widget for file types
file_types_entry = tk.Entry(root)
file_types_entry.pack()

# Frame to hold Start and Stop buttons
buttons_frame = tk.Frame(root)
buttons_frame.pack(fill=tk.X, pady=5)

# Start Filtering Button
start_button = tk.Button(buttons_frame, text="Start Filtering", command=run_filter)
start_button.pack(side=tk.LEFT, padx=(10, 5), expand=True)

# Stop Button
stop_button = tk.Button(buttons_frame, text="Stop", command=stop_process)
stop_button.pack(side=tk.LEFT, padx=(5, 10), expand=True)

# Variable to track the checkbox state (1 for checked, 0 for unchecked)
log_skipped_var = tk.IntVar()

# Create the checkbox
log_skipped_checkbox = tk.Checkbutton(root, text="Write log for skipped files (not optimal disk usage)", variable=log_skipped_var)
log_skipped_checkbox.pack()


root.mainloop()

 

Screenshot 2024-02-19 20.33.10.png

Labots - Usins
  • Patīk 2
Link to comment
Share on other sites

@Usins, vai pareizi saprotu, ka tas skripts vai programma aizkopē konkrētās dienas failus uz atsevišķu folderi? Tādā gadījumā, tas man liekas diezgan neērts risinājums. Vai nevar uzstaisīt, ka tas skripts rada bilžu sourcefile sarakstu notepadā? To tālāk var atvērt jau ar daudziem vīveriem, bez čakara kopēt un dzēst lielus MB vai GB.
Otra lieta, kāda ir ātrdarbība? Piemēram, mans personiskais bilžu folderis pa 20 gadiem ir izaudzis līdz 100 000 bildēm un 200GB. Ja pie katra meklējuma jāpavada pusstunda, stunda, arī garām. Tad jau labāk vienreiz sataisi sarakstu ar exiftool, un manipulē datus ar exceli vai acesu, tā teikt, vienā rāvienā. Bet ja ātrdarbība nav problēma, tad tā programmiņa būtu noderīgs, izmantojams risinājums.

Link to comment
Share on other sites

exe sanāca, bet tam trūka vizualizācijas progresam, ko līdz šim nodrošināja python outputs. 
esmu jauki pavadījis laiku ar GPT. Jāatzīst GPT4 python raksta glīti, reti ir erroru kuru rezultātā kods nedarbojas. Ir daudz pārpratumu, vai 'sekla uzdevuma izpilde' neiedziļinoties būtībā :D  Un pieeja necensties rīkoties optimāli ar datora resursiem. 
Vajadzēs pašam palūgt konspektu, ko šovakar esmu 'iemācijies'. 
papildināts ar counter un current folder, lai var saprast ka darbojas.
 

import os
import shutil
from datetime import datetime
import tkinter as tk
from tkinter import filedialog, Label, Button, Entry, Checkbutton, IntVar, ttk  # Import ttk for combobox
from tkcalendar import DateEntry
from PIL import Image
import threading

root = tk.Tk()
root.title("File search by date and copy")

# Shared variable to control the process
stop_requested = False
processed_files_var = tk.StringVar()
current_folder_var = tk.StringVar()

def get_exif_date_taken(image_path):
    try:
        with Image.open(image_path) as img:
            exif_data = img._getexif()
            if exif_data:
                date_taken = exif_data.get(36867)  # 36867 corresponds to the 'DateTimeOriginal' tag
                if date_taken:
                    return datetime.strptime(date_taken, "%Y:%m:%d %H:%M:%S")
    except (AttributeError, KeyError, IndexError):
        pass
    return None

def filter_and_copy_photos(source_folder, destination_folder, start_date, end_date, include_subfolders, this_period_every_year, date_type, file_types):
    global stop_requested
    # Reset stop_requested in case it was set in a previous run
    stop_requested = False

    start_date = datetime.strptime(start_date, "%d.%m.%Y")
    end_date = datetime.strptime(end_date, "%d.%m.%Y")

    if not os.path.exists(destination_folder):
        os.makestāsti(destination_folder)

    copied_photos_count = 0
    skipped_files = []

    # Nested function to update the GUI
    def update_status(folder, count):
        # Safely update the GUI from the main thread using root.after.
        root.after(0, lambda: processed_files_var.set(f"Processed files: {count}"))
        root.after(0, lambda: current_folder_var.set(f"Current folder: {folder}"))

    # Begin processing the files
    try:
        if include_subfolders:
            for root_dir, stāsti, files in os.walk(source_folder):
                # Check if stop has been requested
                if stop_requested:
                    break
                update_status(root_dir, copied_photos_count)  # Update the status for the current folder
                for filename in files:
                    if stop_requested:
                        break
                    file_path = os.path.join(root_dir, filename)
                    # Existing file processing logic
                    process_file(file_path, start_date, end_date, destination_folder, copied_photos_count, skipped_files, this_period_every_year, date_type, file_types)
                    copied_photos_count += 1  # Increment the copied photos count
                    update_status(root_dir, copied_photos_count)  # Update the GUI with the current count
        else:
            # Process files in the source folder if not including subfolders
            for filename in os.listdir(source_folder):
                if stop_requested:
                    break
                file_path = os.path.join(source_folder, filename)
                # Existing file processing logic
                process_file(file_path, start_date, end_date, destination_folder, copied_photos_count, skipped_files, this_period_every_year, date_type, file_types)
                copied_photos_count += 1  # Increment the copied photos count
                update_status(source_folder, copied_photos_count)  # Update the GUI with the current count
    finally:
        # When the process is stopped or completed, update the GUI accordingly
        update_status("Stopped" if stop_requested else "Completed", copied_photos_count)

    # Log the skipped files if needed
    log_skipped_files(skipped_files)

    print(f"Number of photos copied: {copied_photos_count}")
    print(f"Number of files skipped: {len(skipped_files)}")



def process_file(file_path, start_date, end_date, destination_folder, copied_photos_count, skipped_files, this_period_every_year, date_type, file_types):
    try:
        # Split the custom file types string into a list
        file_extensions = file_types.split(',')

        if os.path.isfile(file_path) and any(file_path.lower().endswith(ext) for ext in file_extensions):
            if not os.path.getsize(file_path) > 0:
                raise IOError("File is online-only or empty")

            date_taken = get_file_date(file_path, date_type)
            if date_taken and is_date_in_range(date_taken, start_date, end_date, this_period_every_year):
                shutil.copy2(file_path, os.path.join(destination_folder, os.path.basename(file_path)))
                copied_photos_count += 1
            else:
                raise ValueError("Missing or invalid date data")
    except (IOError, ValueError) as e:
        skipped_files.append(file_path)
        print(f"Skipped file (Reason - {e}): {file_path}")

def log_skipped_files(skipped_files):
    # Check if logging is enabled
    if log_skipped_var.get() == 1:
        with open("skipped_files_log.txt", "w") as log_file:
            for file_path in skipped_files:
                log_file.write(f"{file_path}\n")


def select_folder(label):
    folder_selected = filedialog.askdirectory()
    label.config(text=folder_selected)
    return folder_selected

def threaded_filter_and_copy():
    start_date_str = start_date_entry.get_date().strftime('%d.%m.%Y')
    end_date_str = end_date_entry.get_date().strftime('%d.%m.%Y')
    filter_and_copy_photos(source_label['text'], destination_label['text'], start_date_entry.get(), end_date_entry.get(), subfolders_var.get(), this_period_var.get(), date_option_var.get(), file_types_entry.get())

def run_filter():
    # Starting the filter process in a separate thread
    threading.Thread(target=threaded_filter_and_copy).start()
    
def is_date_in_range(date_taken, start_date, end_date, this_period_every_year):
    if this_period_every_year:
        # Adjust the year of the date taken to match the start date for comparison
        date_taken_adjusted = date_taken.replace(year=start_date.year)
        return start_date <= date_taken_adjusted <= end_date
    else:
        return start_date <= date_taken <= end_date

def get_file_date(file_path, date_type):
    if date_type == 'date_modified':
        return datetime.fromtimestamp(os.path.getmtime(file_path))
    elif date_type == 'date_created':
        return datetime.fromtimestamp(os.path.getctime(file_path))
    elif date_type == 'exif_date_taken':
        return get_exif_date_taken(file_path)
    else:
        return None
    
def stop_process():
    global stop_requested
    stop_requested = True




source_label = Label(root, text="Select source folder")
source_label.pack()

Button(root, text="Browse", command=lambda: select_folder(source_label)).pack()

destination_label = Label(root, text="Select destination folder")
destination_label.pack()

Button(root, text="Browse", command=lambda: select_folder(destination_label)).pack()

start_date_label = tk.Label(root, text="Date from (included):")
start_date_label.pack()
start_date_entry = DateEntry(root, date_pattern='dd.mm.yyyy')
start_date_entry.pack()

end_date_label = tk.Label(root, text="Date to (NOT included):")
end_date_label.pack()
end_date_entry = DateEntry(root, date_pattern='dd.mm.yyyy')
end_date_entry.pack()


subfolders_var = IntVar()
Checkbutton(root, text="Include subfolders", variable=subfolders_var).pack()

this_period_var = IntVar()
Checkbutton(root, text="This period every year", variable=this_period_var).pack()

# Define the options for the dropdown menu
date_options = ['date_modified', 'date_created', 'exif_date_taken']
date_option_var = tk.StringVar()
date_option_dropdown = ttk.Combobox(root, textvariable=date_option_var, values=date_options)
date_option_dropdown.current(0)  # Default to the first option ('date_modified')
date_option_dropdown.pack()

# Add label for file types entry
file_types_label = tk.Label(root, text="Enter file types, comma separated (e.g., jpg,jpeg,png):")
file_types_label.pack()

# Add entry widget for file types
file_types_entry = tk.Entry(root)
file_types_entry.pack()

# Frame to hold Start and Stop buttons
buttons_frame = tk.Frame(root)
buttons_frame.pack(fill=tk.X, pady=5)

# Start Filtering Button
start_button = tk.Button(buttons_frame, text="Start Filtering", command=run_filter)
start_button.pack(side=tk.LEFT, padx=(10, 5), expand=True)

# Stop Button
stop_button = tk.Button(buttons_frame, text="Stop", command=stop_process)
stop_button.pack(side=tk.LEFT, padx=(5, 10), expand=True)

# Variable to track the checkbox state (1 for checked, 0 for unchecked)
log_skipped_var = tk.IntVar()

# Create the checkbox
log_skipped_checkbox = tk.Checkbutton(root, text="Write log for skipped files (not optimal disk usage)", variable=log_skipped_var)
log_skipped_checkbox.pack()

processed_files_label = tk.Label(root, textvariable=processed_files_var)
processed_files_label.pack()

current_folder_label = tk.Label(root, textvariable=current_folder_var)
current_folder_label.pack()

root.mainloop()


te, (uz mana datora LOL) strādājošs exe.
https://www.dropbox.com/scl/fi/g0heaqkn8y5ro27xbt41t/search_files_by_dates_pick.exe?rlkey=fvsc4g87l302l47q8uqlxusr8&dl=0

šitam tagad loga izmērs raustās pieskaņojoties aktuālā foldera nosaukuma garumam, :D

Link to comment
Share on other sites

Izveido kontu, vai pieraksties esošajā, lai komentētu

Jums ir jābūt šī foruma biedram, lai varētu komentēt tēmas

Izveidot jaunu kontu

Piereģistrējies un izveido jaunu kontu, tas būs viegli!

Reģistrēt jaunu kontu

Pierakstīties

Jums jau ir konts? Pierakstieties tajā šeit!

Pierakstīties tagad!
 Share

×
×
  • Izveidot jaunu...