Merhabalar THT ailesi bugün flet kütüphanesini anlatacam öyle kuru kuru anlatılmaz diyip basit bir Youtube Video İndirici projesi ile anlatımı süsleyeyim dedim fazla uzatmadan iyi okumalar.


1.Adım:Flet kütüphanesi nedir ve ne işe yarar

Flet;web,masaüstü ve mobil uygulamar geliştirirken kullanılabilen bir kütüphanedir kullanımı fluttera benziyor web alanında ise HTML,CSS,JavaScript bilmeden web uygulamaları geliştirebilmenize olanak sağlıyor en sevdiğim özelliği ise çapraz platform desteği ile geliştirdiğiniz uygulamayı ister Windows ister macOs ister Linux isterseniz de Tarayıcıda çalışsın hiç fark etmez şimdi diyorsunuz ki nasıl o halde sözü fazla uzatmadan uygulamamızı geliştirmeye başlayalım
2.Adım Gerekli kütüphaneleri import edelim
Projemiz Youtube video indirici olduğu için bir youtube video indirme kütüphanesi ,flet ,os birde time kütüphanelerini kullanacaz
Burda yt_dlp adlı kütüphaneyi kullanacaz videoları indirmek için
Python:
import flet as ft
import yt_dlp
import time
import os
Burda yt_dlp adlı kütüphaneyi kullanacaz videoları indirmek için
pip install flet
pip install fletpip install yt_dlp
pip install yt_dlp3.Adım Kodlama Aşaması
Python:
def main(page: ft.Page):
Programımızın temel arayüzü şu olacak iki sekme olacak ilk sekmede video indirme yeri ikinci sekmede video indirme yeri
Python:
page.title = "YouTube Video İndirici"
downloadFiles = []
burda uygulamamız açılırken üstteki yazıyı ayarlamak için title modülünü kullandık alttaki downloadFiles listemizi de indirilen videoların kaydını tutsun diye atadık
şimdiiii 4 fonksiyon atayacaz ve hepsi de main fonksiyonun içinde
Python:
def search_video(e):
pass
def download_video(e):
pass
def update_downloaded_files():
pass
def progress_hook(d):
pass
burda parametre olarak atadığım e kullanıcı etkileşimli olacak anlamında.
Şimdi ne yapacaz fonksiyonları mı dolduracaz tabiki hayır ÖNCE TASARIMI HALLETMELİYİZ SONRA ADIM ADIM FONKSİYONLARI halledecez tasarım kodları full ingilizce zaten anlaşılıyor bu çok güzel birşey ingilizce bilenler için.
Python:
aramaGirdisi = ft.TextField(label="Video Arama", expand=True)
search_button = ft.ElevatedButton(text="Ara", on_click=search_video)
videoTitle = ft.Text(value="", size=24)
thumbnail = ft.Image(src="", width=400, height=300)
format_dropdown = ft.Dropdown(
label="Format",
options=[
ft.dropdown.Option("mp4"),
ft.dropdown.Option("mp3"),
ft.dropdown.Option("webm"),
],
value="mp4"
)
resolution_dropdown = ft.Dropdown(
label="Çözünürlük",
options=[
ft.dropdown.Option("1080p"),
ft.dropdown.Option("720p"),
ft.dropdown.Option("480p"),
ft.dropdown.Option("360p"),
],
value="1080p"
)
download_button = ft.ElevatedButton(text="İndir", on_click=download_video)
downloading_text = ft.Text(value="Video indiriliyor...", size=24, visible=False)
progress_bar = ft.ProgressBar(value=0, width=400, visible=False)
progress_text = ft.Text(value="0%", size=20, visible=False)
searchContainer = ft.Container(
content=ft.Row(
controls=[aramaGirdisi, search_button],
alignment=ft.MainAxisAlignment.CENTER
),
alignment=ft.alignment.center,
height=page.height
)
videoInfoContainer = ft.Container(
content=ft.Column(
controls=[
videoTitle,
thumbnail,
ft.Row(
controls=[download_button, format_dropdown, resolution_dropdown],
alignment=ft.MainAxisAlignment.CENTER,
spacing=10
),
downloading_text,
progress_bar,
progress_text
],
alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
expand=True
),
alignment=ft.alignment.center,
expand=True,
visible=False
)
files_list = ft.Column(
controls=[],
spacing=10,
horizontal_alignment=ft.CrossAxisAlignment.START
)
tabs = ft.Tabs(
selected_index=0,
animation_duration=300,
tabs=[
ft.Tab(
text="Video İndir",
content=ft.Column(
controls=[searchContainer, videoInfoContainer],
alignment=ft.alignment.center
),
),
ft.Tab(
text="İndirilenler",
content=ft.Container(
content=files_list,
alignment=ft.alignment.center,
expand=True
),
),
],
expand=1,
)
page.add(tabs)
tüm tasarım kodlarımız bu gözünüz korkmasın adım adım gidecez.Tasarım planımız şu öncelikle sayfanın ortasında bir arama çubuğu olsun kullanıcı etkileşimli tabiki , yanında da ara butonu olsun
arama yapıldığında arama çubuğu ve ara butonu kaybolsun ve kullanıcının arattığı videonun kapak resmi sayfanın ortasında üstünde de video hakkında etiketler fotonun altında ise indirme türünü seçebileceği bir bar ve hemen yanında çözünürlük tercihi yapılabilecek bir bar altta ise indir butonu.Kullanıcı indire bastığında en altta indirme yüzdesi ve indirme çubuğu gözükecek planımız bu kadar
Python:
aramaGirdisi = ft.TextField(label="Video Arama", expand=True)
search_button = ft.ElevatedButton(text="Ara", on_click=search_video)
videoTitle = ft.Text(value="", size=24)
thumbnail = ft.Image(src="", width=400, height=300)
format_dropdown = ft.Dropdown(
label="Format",
options=[
ft.dropdown.Option("mp4"),
ft.dropdown.Option("mp3"),
ft.dropdown.Option("webm"),
],
value="mp4"
)
resolution_dropdown = ft.Dropdown(
label="Çözünürlük",
options=[
ft.dropdown.Option("1080p"),
ft.dropdown.Option("720p"),
ft.dropdown.Option("480p"),
ft.dropdown.Option("360p"),
],
value="1080p"
)
download_button = ft.ElevatedButton(text="İndir", on_click=download_video)
downloading_text = ft.Text(value="Video indiriliyor...", size=24, visible=False)
progress_bar = ft.ProgressBar(value=0, width=400, visible=False)
progress_text = ft.Text(value="0%", size=20, visible=False)
Bu kısım planda bahsettiğimiz arama çubuğu ,ara butonu(tıklandığında search_video fonksiyonu çalışacak şekilde tanımladım),video başlığı,video kapak fotosu(thumbnail),indirme uzantısı seçme alanı,çözünürlük seçme alanı,indirme butonu(tıklandığında download_video fonksiyonu çalışacak şekilde ayarladım) ve progress barları birer birer bir değişkene atadık bakın daha henüz sayfaya yerleştirmedik sadece işimiz kolaylaşsın diye değişkenlere atadık.Hadi sayfada yerleştirmeye başlayalım.
Python:
searchContainer = ft.Container(
content=ft.Row(
controls=[aramaGirdisi, search_button],
alignment=ft.MainAxisAlignment.CENTER
),
alignment=ft.alignment.center,
height=page.height
)
burda searchContainer diye bir container atadık.Container ne diye soracak olursanız şöyle düşünün elinizde bir kutu var ve kutunun içinde de kalem var siz şimdi bu kutuyu salondan alıp mutfağa taşırsanız kalemi de taşımışsınız anlamına gelir işte container tam anlamıyla burdaki kutu ve tamamen bu işlevde kullanıyoruz.Container ın içine arama çubuğunu atadığımız değişkeni ve ara butonunu yerleştiriyoruz ve bu containerı sayfanın tam ortasına genişliği ise sayfanın genişliği kadar olmasını sağlıyoruz ha unutmadan burdaki Row da yatayda aynı hizada olsun diye aynı satırda olması için kullandık
Python:
videoInfoContainer = ft.Container(
content=ft.Column(
controls=[
videoTitle,
thumbnail,
ft.Row(
controls=[download_button, format_dropdown, resolution_dropdown],
alignment=ft.MainAxisAlignment.CENTER,
spacing=10
),
downloading_text,
progress_bar,
progress_text
],
alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
expand=True
),
alignment=ft.alignment.center,
expand=True,
visible=False
)
Bu kısım ise kullanıcı arama yaptıktan sonra arama barı ve butonu kaybolacak ya işte o an karşımıza gelecekleri gösterecez
Ne yapacaz tabiki de önce kutumuzu yani Container ımızı ayarlayacaz bunun içine de bir Sütun yani Colmn ayarlayacaz sayfayı sütunlara ayırdığınızı hayal edin en ortadaki sütunu seçtiniz ve o orta sütunun da ortasını seçtiniz nedir bu Tam da Merkez noktası olur işte o merkezde aynı hizaya indirme butonunu ,çözünürlük seçme menüsünü ve indirme uzatısı seçme menüsünü ekliyoruz ve bunların hemen üstüne video kapak fotosu ve onun da üstüne video başlığını ekliyoruz
Python:
tabs = ft.Tabs(
selected_index=0,
animation_duration=300,
tabs=[
ft.Tab(
text="Video İndir",
content=ft.Column(
controls=[searchContainer, videoInfoContainer],
alignment=ft.alignment.center
),
),
ft.Tab(
text="İndirilenler",
content=ft.Container(
content=files_list,
alignment=ft.alignment.center,
expand=True
),
),
],
expand=1,
)
page.add(tabs)
burda ise uygulamamızın sekmeleri hani video indirme sekmesi ve indirilenleri görme sekmesi olacak ya onları ayarlayacaz burda da container mantığı var artık anlamışsınızdır zaten , burda content diye bir mevzu var ve bu mevzunun içinde de controls diye birşey var burası bu sekmede neler gösteileceğini içerir biz buraya video indirme sekmesinde searchContainer ve videoInfoContainer adlı containerlarımızda ayarladıklarımızı göstermek istiyoruz o yüzden oraya yazıyoruz aynı şekilde indirilenler kısmında da file_list containerımızın gösterilmesini istedik burda selected_index=0 yani uygulama açılınca varsayılan olarak video indirme sekmesi gözüksün diye yaptık eğer 1 yapsaydık indirilenler sekmesi gözükürdü
En alttaki page.add(tabs) ise sekme sistemini sayfaya eklemeye yarar .Eveeeeeet tasraım aşaması bitti şimdi fonksiyonları dolduralım
Python:
def search_video(e):
sorgu = aramaGirdisi.value
ydl_opts = {
'format': 'best',
'noplaylist': True,
'quiet': True,
'skip_download': True
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
try:
infoDict = ydl.extract_info(f"ytsearch:{sorgu}", download=False)['entries'][0]
videoTitle.value = f"Başlık: {infoDict['title']}"
thumbnail.src = infoDict['thumbnail']
searchContainer.visible = False
videoInfoContainer.visible = True
page.update()
except Exception as e:
videoTitle.value = f"Video bulunamadı{e}."
thumbnail.src = ""
page.update()
print(f"Arama hatası: {e}")
Burda arama çubuğunda girilen inputu sorgu değişkeniine atadık ydl_ops ise bir akım ayarlamları yapacağımız küme çünkü evet ayarlamalarımızı yt_dlp.YoutubeDL içine entegre ettik ve kısa olsun diye ydl olarak adlandırdık sürekli uzun uzun yazmayalım diye yaptık bu işlemleri .Hah işte geldi en çok hata aldığım yer o kadar hata aldım ki artık try except kullandım burada video bilgilerini alıp page.update ile tasarım aşamsında güncel durumları göstermesini istedik aynı zamanda bir hata alırsak da resimsiz bir ekran ve video bulunamadı hata mesajını göstermsini istedik
Python:
def download_video(e):
download_button.visible = False
downloading_text.visible = True
progress_bar.visible = True
progress_text.visible = True
video_url = aramaGirdisi.value
format_selection = format_dropdown.value
resolution = resolution_dropdown.value
search_url = f"ytsearch:{video_url}"
ydl_opts = {
'format': format_selection,
'outtmpl': f'./downloads/%(title)s.%(ext)s',
'noplaylist': True,
'progress_hooks': [progress_hook]
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info(search_url, download=True)
ext = info_dict.get('ext', 'mp4')
downloadFiles.append(info_dict['title'] + '.' + ext)
for i in range(101):
time.sleep(0.05)
progress_bar.value = i
progress_text.value = f"{i}%"
page.update()
download_button.visible = True
downloading_text.visible = False
progress_bar.visible = False
progress_text.visible = False
update_downloaded_files()
except Exception as e:
downloading_text.value = "İndirme hatası."
print(f"İndirme hatası: {e}")
page.update()
burda ise fonksiyon çalıştığında yani indir butonu tıklandığında ilk olarak buton devre dışı kalmasını istdik nedençünkü art arda indirme olmasın ondan sonra indirme yüzdesi indiriliyor yazısı ve indirme barı gözüksün dedik videu url olarak arama sekmesinden inputu alıp url yi ayarlayacak çözünürlüğü uzantıyı hepsini kullanıcının girdiği değerleri alacak ve videoyu indirecek ayreten bir döngü de kurduk progress bar için 0 dan yüze kadar sayı yazdırsın diye en altta ise indirme işlemi bittiğinde indir butonu tekrar aktif olsun indiriliyor yazısı ,indirme yüzdesi,progress bar bunların tekrar kaybolmasını istedik ve indirilenler sekmesine indirilen dosyanın bilgisinin geçmesini istedik
Python:
def update_downloaded_files():
files_list.controls = [ft.Text(value=file, size=18) for file in downloadFiles]
page.update()
Bu fonksiyonu indirilenler sekmesine veri kaydı yapılsın diye atadık zaten mantığı basit downloadFile listesindeki verileri oku text cinsinden indirilenler sekmesine yazdır bu kadar
Python:
def progress_hook(d):
if d['status'] == 'downloading':
percent = int(d['downloaded_bytes'] / d['total_bytes'] * 100)
progress_bar.value = percent
progress_text.value = f"{percent}%"
page.update()
Son olarak da progress barın yüzdeliğini hesaplamak için yaptık bu fonksiyonu mantık basit dört işlem indirme devam ediyorsa inen byte sayısının toplam byte sayısına bölümü ve 100 ile çarpımı bu kadar
Python:
if not os.path.exists('./downloads'):
os.makedirs('./downloads')
ft.app(target=main)
Download klasörü yoksa oluşturmasını istedik veeeee ana fonksiyonumuz olan main fonksiyonundan çıkıp ft.app(target=main) diyerek uygulamamızı çalıştırıyoruz
Buraya kadar okuduysanız gözlerinize sağlık ee artık alırız bir emojinizi kodlara ulaşmak isterseniz Github
Görseller:





