From d69767f5e97ea3428a185a2333bb9e793e20233f Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Fri, 30 May 2025 00:34:33 +0500 Subject: [PATCH] Remove bbolt database dependency Replaced bbolt-based database handling with Fyne built-in preferences for storing application settings. Deleted migration logic, database initialization, and error handling related to bbolt, simplifying the codebase and reducing external dependencies. --- FyneApp.toml | 5 +- README.md | 1 - convertor/repository.go | 24 ++++---- convertor/view/conversion.go | 7 +-- data/.gitignore | 2 - db/db.go | 7 --- error/view.go | 38 +----------- go.mod | 1 - go.sum | 4 -- handler/convertor.go | 18 +++--- handler/main.go | 6 +- handler/menu.go | 7 +-- images/screenshot-folder-structure.png | Bin 7544 -> 6871 bytes languages/active.en.toml | 8 --- languages/active.kk.toml | 8 --- languages/active.ru.toml | 2 - localizer/repository.go | 8 +-- main.go | 64 ++------------------ migration/migration.go | 12 ---- setting/directory_for_saving.go | 8 +-- setting/entity.go | 4 +- setting/repository.go | 79 ++++++------------------- theme/repository.go | 8 +-- theme/theme.go | 5 +- 24 files changed, 67 insertions(+), 259 deletions(-) delete mode 100644 data/.gitignore delete mode 100644 db/db.go delete mode 100644 migration/migration.go diff --git a/FyneApp.toml b/FyneApp.toml index 1fe1f80..b30925b 100644 --- a/FyneApp.toml +++ b/FyneApp.toml @@ -3,7 +3,4 @@ Name = "GUI for FFmpeg" ID = "net.kor-elf.projects.gui-for-ffmpeg" Version = "0.9.0" - Build = 4 - -[Migrations] - fyneDo = true \ No newline at end of file + Build = 11 diff --git a/README.md b/README.md index 8f59d1f..1d3cc54 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ 7. Создаться папка **fyne-cross/bin** и там будет созданна папка с тем названием под которую Вы компилировали приложения (linux-amd64 или windows-amd64). 8. В папку **fyne-cross/bin/linux-amd64** или **fyne-cross/bin/windows-amd64** копируете: * icon.png - * data * languages * LICENSE * LICENSE-3RD-PARTY.txt diff --git a/convertor/repository.go b/convertor/repository.go index f751607..d052ff3 100644 --- a/convertor/repository.go +++ b/convertor/repository.go @@ -5,12 +5,12 @@ import ( ) type RepositoryContract interface { - GetPathFfmpeg() (string, error) - SavePathFfmpeg(code string) (setting.Setting, error) - GetPathFfprobe() (string, error) - SavePathFfprobe(code string) (setting.Setting, error) - GetPathFfplay() (string, error) - SavePathFfplay(code string) (setting.Setting, error) + GetPathFfmpeg() string + SavePathFfmpeg(code string) setting.Setting + GetPathFfprobe() string + SavePathFfprobe(code string) setting.Setting + GetPathFfplay() string + SavePathFfplay(code string) setting.Setting } type Repository struct { @@ -21,26 +21,26 @@ func NewRepository(settingRepository setting.RepositoryContract) *Repository { return &Repository{settingRepository: settingRepository} } -func (r Repository) GetPathFfmpeg() (string, error) { +func (r Repository) GetPathFfmpeg() string { return r.settingRepository.GetValue("ffmpeg") } -func (r Repository) SavePathFfmpeg(path string) (setting.Setting, error) { +func (r Repository) SavePathFfmpeg(path string) setting.Setting { return r.settingRepository.CreateOrUpdate("ffmpeg", path) } -func (r Repository) GetPathFfprobe() (string, error) { +func (r Repository) GetPathFfprobe() string { return r.settingRepository.GetValue("ffprobe") } -func (r Repository) SavePathFfprobe(path string) (setting.Setting, error) { +func (r Repository) SavePathFfprobe(path string) setting.Setting { return r.settingRepository.CreateOrUpdate("ffprobe", path) } -func (r Repository) GetPathFfplay() (string, error) { +func (r Repository) GetPathFfplay() string { return r.settingRepository.GetValue("ffplay") } -func (r Repository) SavePathFfplay(path string) (setting.Setting, error) { +func (r Repository) SavePathFfplay(path string) setting.Setting { return r.settingRepository.CreateOrUpdate("ffplay", path) } diff --git a/convertor/view/conversion.go b/convertor/view/conversion.go index 947a37c..7959bd9 100644 --- a/convertor/view/conversion.go +++ b/convertor/view/conversion.go @@ -353,7 +353,7 @@ func newDirectoryForSaving(app kernel.AppContract, settingDirectoryForSaving set locationURI, err = storage.ListerForURI(r) if err == nil { - _, _ = settingDirectoryForSaving.SaveDirectoryForSaving(locationURI.Path()) + _ = settingDirectoryForSaving.SaveDirectoryForSaving(locationURI.Path()) } }, locationURI) @@ -363,10 +363,7 @@ func newDirectoryForSaving(app kernel.AppContract, settingDirectoryForSaving set } func getDirectoryForSaving(settingDirectoryForSaving setting.DirectoryForSavingContract) (fyne.ListableURI, error) { - path, err := settingDirectoryForSaving.GetDirectoryForSaving() - if err != nil { - return nil, err - } + path := settingDirectoryForSaving.GetDirectoryForSaving() if len(path) > 0 { path = "file://" + path diff --git a/data/.gitignore b/data/.gitignore deleted file mode 100644 index c96a04f..0000000 --- a/data/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file diff --git a/db/db.go b/db/db.go deleted file mode 100644 index ce2ddf9..0000000 --- a/db/db.go +++ /dev/null @@ -1,7 +0,0 @@ -package db - -import "errors" - -var ( - ErrRecordNotFound = errors.New("record not found") -) diff --git a/error/view.go b/error/view.go index 2d68856..50182e6 100644 --- a/error/view.go +++ b/error/view.go @@ -1,14 +1,12 @@ package error import ( - "errors" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer" "github.com/nicksnyder/go-i18n/v2/i18n" - "go.etcd.io/bbolt" ) type ViewContract interface { @@ -37,17 +35,10 @@ func (v View) PanicError(err error) { MessageID: "error", }) - messagetText := err.Error() - if errors.Is(err, bbolt.ErrTimeout) { - messagetText = v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorDatabaseTimeout", - }) - } - v.app.GetWindow().SetContent(container.NewBorder( container.NewVBox( widget.NewLabel(messageHead), - widget.NewLabel(messagetText), + widget.NewLabel(err.Error()), ), nil, nil, @@ -57,30 +48,3 @@ func (v View) PanicError(err error) { }), )) } - -func (v View) PanicErrorWriteDirectoryData() { - if v.isSetLanguage { - v.isSetLanguage = false - _ = v.app.GetLocalizerService().SetCurrentLanguageByCode(lang.SystemLocale().LanguageString()) - } - - message := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorDatabase", - }) - messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "error", - }) - - v.app.GetWindow().SetContent(container.NewBorder( - container.NewVBox( - widget.NewLabel(messageHead), - widget.NewLabel(message), - ), - nil, - nil, - nil, - localizer.LanguageSelectionForm(v.app.GetLocalizerService(), func(lang kernel.Lang) { - v.PanicErrorWriteDirectoryData() - }), - )) -} diff --git a/go.mod b/go.mod index 721120e..1bbe1ff 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/BurntSushi/toml v1.5.0 github.com/nicksnyder/go-i18n/v2 v2.6.0 github.com/ulikunitz/xz v0.5.12 - go.etcd.io/bbolt v1.4.0 golang.org/x/image v0.27.0 golang.org/x/text v0.25.0 ) diff --git a/go.sum b/go.sum index 9db999c..2efe54d 100644 --- a/go.sum +++ b/go.sum @@ -67,14 +67,10 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo= github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= diff --git a/handler/convertor.go b/handler/convertor.go index a0cd29b..8643d35 100644 --- a/handler/convertor.go +++ b/handler/convertor.go @@ -61,9 +61,9 @@ func (h ConvertorHandler) MainConvertor() { } func (h ConvertorHandler) FfPathSelection() { - ffmpeg, _ := h.convertorRepository.GetPathFfmpeg() - ffprobe, _ := h.convertorRepository.GetPathFfprobe() - ffplay, _ := h.convertorRepository.GetPathFfplay() + ffmpeg := h.convertorRepository.GetPathFfmpeg() + ffprobe := h.convertorRepository.GetPathFfprobe() + ffplay := h.convertorRepository.GetPathFfplay() h.convertorView.SelectFFPath(ffmpeg, ffprobe, ffplay, h.saveSettingFFPath, h.MainConvertor, h.downloadFFmpeg) } @@ -122,9 +122,9 @@ func (h ConvertorHandler) checkingFFPathUtilities() bool { if ffplayChecking == false { continue } - _, _ = h.convertorRepository.SavePathFfmpeg(item.FFmpeg) - _, _ = h.convertorRepository.SavePathFfprobe(item.FFprobe) - _, _ = h.convertorRepository.SavePathFfplay(item.FFplay) + _ = h.convertorRepository.SavePathFfmpeg(item.FFmpeg) + _ = h.convertorRepository.SavePathFfprobe(item.FFprobe) + _ = h.convertorRepository.SavePathFfplay(item.FFplay) return true } @@ -156,9 +156,9 @@ func (h ConvertorHandler) saveSettingFFPath(ffmpegPath string, ffprobePath strin return errors.New(errorText) } - _, _ = h.convertorRepository.SavePathFfmpeg(ffmpegPath) - _, _ = h.convertorRepository.SavePathFfprobe(ffprobePath) - _, _ = h.convertorRepository.SavePathFfplay(ffplayPath) + _ = h.convertorRepository.SavePathFfmpeg(ffmpegPath) + _ = h.convertorRepository.SavePathFfprobe(ffprobePath) + _ = h.convertorRepository.SavePathFfplay(ffplayPath) h.MainConvertor() diff --git a/handler/main.go b/handler/main.go index 0e97c6f..e8a669b 100644 --- a/handler/main.go +++ b/handler/main.go @@ -28,9 +28,9 @@ func NewMainHandler( } func (h MainHandler) Start() { - language, err := h.localizerRepository.GetCode() - if err != nil { - err = h.app.GetLocalizerService().SetCurrentLanguageByCode(lang.SystemLocale().LanguageString()) + language := h.localizerRepository.GetCode() + if len(language) == 0 { + err := h.app.GetLocalizerService().SetCurrentLanguageByCode(lang.SystemLocale().LanguageString()) if err != nil { h.menuHandler.LanguageSelection() return diff --git a/handler/menu.go b/handler/menu.go index fa32d8b..7bc8b87 100644 --- a/handler/menu.go +++ b/handler/menu.go @@ -143,7 +143,7 @@ func (h MenuHandler) openAbout() { func (h MenuHandler) LanguageSelection() { h.localizerView.LanguageSelection(func(lang kernel.Lang) { - _, _ = h.localizerRepository.Save(lang.Code) + _ = h.localizerRepository.Save(lang.Code) h.convertorHandler.MainConvertor() }) } @@ -154,10 +154,7 @@ func (h MenuHandler) settingsSelection() { if err != nil { return err } - _, err = h.localizerRepository.Save(setting.Language.Code) - if err != nil { - return err - } + _ = h.localizerRepository.Save(setting.Language.Code) err = h.themeService.SetCurrentTheme(setting.ThemeInfo) if err != nil { diff --git a/images/screenshot-folder-structure.png b/images/screenshot-folder-structure.png index 19b2a3f82e9d0cf9f703136e7340762f5c41fb49..1e29071ac4a6242718a6b0381ecbbd6502454028 100644 GIT binary patch literal 6871 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 NP*Kp3uaYwh`F|wlnpXe- literal 7544 zcmaKRbx>T*v-Sc(0t5*j+}#Pjkl+#^KyY_w6C}Xmn&5;4_h2E&23=f2aCf&Li!Lt9 zB6r`n>ig!?pEFGWKyU68$(kClfVz`-5lX2zXb z0RU+6R25#o^UXe5@d+Tgq{cph@fA8{i8y_=Z}8G_R39S3MgNYU!m6$)O8P|cCWX`F zRTM**nxew%Ep|m*Av+vd(|!(l{m=%|-H?)Er#(8Vw3an`&SH&P9KFxd#}xb)B8XK_ zpZ%%h$^f|v<9b{oIljpL;o*SEr4S;ph=RBG<=0iLi19+kybCOb2s)}OUb(7I_u_b^ zB_*k;shMpYL^w=LOgLe_*@HNY)Y-BfZ9(^7KR>@TJ~Ne?)uO<=n-5V|!&v@cu#%$U z0>5(w7svEdyH!(I!_xwX@JK|%&!79d6NMPq1a)n;Mn*>E`jvx2LkI2s8}AqEUFP1O zAkJa5^OTY{W_juN7-NsmDQu5!R({L~Nw3<_vNCL)@%X%wlS2p0yRYo;@3;A1XKGZE zDjLyLyyK!>qC1an&ixr!C`Zn5Wq#X;(O2z^b1!9-({$Ltr>O1LjQG%aW%Ai-H~Ebi ztN{|l7`|#*TTKiWotOMux6+K?y*zuT=f+{>s+A0oCa_Sw_;pn!b6!eV%D)P}`&i6S z6bUMrDBsy8!Lx|DXJhS%;dYzw?``I1Yak{9SPp+a-X|mMOvv^>8_up_P3>j406D;9 z-pa}SUTyQQY;(!?l9aF41i=!!@@=XO_0>!fwvLCIqv^+EMdlK>3)GDDQ$p+>(*~dT zmIu{N<$wjMULy92I{nA3mZ=0h8f^GrHaQ)Ib(N?f) zJHhXGOA|gh*5k#DRW7~{XX>FWA+1`xb9~@srm?J1d1#`t3BLg&J9+n(oYUKV)Eel(v*g zRrO$l26BxlK>mz#jybVhKYoxq9phZMLj+C}-JwZ@Dd9h=hK(dg{R@muI_PRw?7y^J zW7+}>j1e==o&Pzfsn4wY_Iz#D%KFeVg66X5oaQn(VBAK!8gUh6AOI#~R0g{FCEze3 zO}S7ro&Xvk>`MfZ!rKz&gLeq)9jm2aof&r8mN&(+)xT_hn6jKCM|siIK3Gd&0H#yC z!}AjHll<_Wtp5CAF6`s5>!LIjSvs2#Se;?YLZo(=38lYTz{CY`1=BD^X9xD8%vtk& zVUb4Zqth?_Wn*H1%_@XD%A3F-rbi>!-)4{h+7ZCz@>n6Gtp?(!SDd0C&zsBw0p%Xk z*>hlqiNyg*%?y`^l{|S10 zb0aSI%FlxQ>;^to+9uU1EYG92!BhfC0%rGHhVVA zFpU`zSMRM-{pXX;+5l)A=&B`CT6fZpuyc#;sU)*fwbLYQ>1Wedlt&gzS%nf190U=n)eV;Rb7w<=Eeh6;oMJ|ijGbA!p6UE3 zzTTz(gwnz$y1TpoMW26xh&_O#YaNw8e!MN8NlCe+n;IDy;N<1KJU=JFjRd^a(@RN9 zqoAd&)G50Oc-VC3k;4Jd!5-Yy)M6kI$Qw3cpua14c*0Sn#*inzl$DibWi76#81C+7W@m?;Z4V(3 z2nh)Ztndv~KfZAS2e_GiY*L6?`$Yz}I8DM^jv}YU3(LZm7AcJ>X}Uy~0p#^@tipGt z1=AA8wfXUNVyTz*c|8-w?7iL~J_=lgL4(aH*Bx_1EB#PTnc9!ONQUUNPIlkr_UCuI zgy5gz!9nWU?^cTN>Wu?oqqKl=M-h)E8h~S_XxsO<94`(Kwqf)WVjp?PSSzkc1;1W* zBXtRe4CplI=ObprAj}!!ml+0@eEfl+y(7A%Q95`2>HU?6n{gr3%4<-p@UCg-_VzY< z=&Q1KgSIs-KF2#dJN9;VWMpLZjuYQ&YHAuA)4Mk)@uPYd%-!en5qK|L2jjWt(i8Fs z+&DKSjAkp)b-fr6nwG~KEAoF5FCz(X?t$5|n(aQv=2li#PEJm0YPj73R8&-`b3?Hn z{O%t!`${SABh?@DMeqTSE&X-ra)THOP+4NIC zo%fCVBkSmxqLac=h=zB)M_wIORPeN!=cT5W=$AWXrmC=nhK4E?3Q9;IdgG~`>u*ss zg;deb-rl(CtiL*L_GE4OBLu}d#Qo*@Ki;M>V+CtZ9v_g*LGD^P?uO%DPZ5v{0(zJ6 zI?V7g?_u#37oJk=P!U4g3f-<}fO+1XY#tsyK0fJ}V&6F~505@8I)Lm;?yJA|^o+@B z#Ur*(iVqybt~pmJp?{&y`7h_jmhrRv@Al(ef`cdy{B~D@rU)s7Q#~0ol$Pu+4n5jI z$7>ys8E18M^(NCQrNROydkhXf7S@By=Vzc^B38 zT6!BCEsVsyGZBkOA)0sbkZY~UI0rEu<8lf#pOLNZsz)(G?WEjoRG zJO)%RqRQBzA%m`9QzaH{*Jrt57_|OhYQL|H2>>0@tMOhfrqz{BF&<7M-ky^Na)GbZL1 zEm>j_P+xPG&~IzLqKSPz<}CqOo|bu&huyn(?@BduaKbn^Ih#DT)lf>wZ`O);~w^!2OX5s6CpuhJ}e)Tw3ZW zPD4*mAFs5zxoHE|Ul#T9R-&Ck)w-YqtHI;5!0s4T^e*AjRdqWqLEH~Rk|r2o%> z(UQsTe(4RShxYAxGe+$HMxeOsl(-fQPMgYxgg?D@#G7K_Ddq7Md#o74z0zu-k8kENp$8c zo3HqdM;#P!7N*4m4|X&M?@-fFjdcNP{jRxv;)9RT+1Qmm(;%(}ZlsWe1L5TZiki z7c)!oCq!|}_(dg%6Jv8n6A<#65DRD{BJ-E3TrlwdL~M@yD&g_cc|Wp{n*AfuBx;$x zu<2T8Cck)!hw$a_b+;w5C?ooMzjD~P`FlO4pe1orrqY3lAHZb4FZH$-qg$e2Vp+K@ z`Hk69`!-}pcW!i_Y7gQ#+3GDEePpNz=*mvju6=s1!t_O$v+-QDq0%pZa(Hw0X}`7v=%x!*^~6Tn-g<6YfEg`9`0qN8Do2Vt31S0yIaa zkWFFA>IP5n4HfvXJJ_CMkbgNqh>b!~;kQ*TGb&a)4q(^<|Emo1(ElF9^&yZ@l*M+g zZ?37CgsY&)L5f-r>^#SuLhc72>=?tWBgl*^WzbeAx$dRYlt@lq9%RCiiJ3Xa1Qiy> zou9j@s;c_>Hkw1AQaU{3rKP1(ZA=kKR50W$ik8I_^vH?JJZId(fkD)@NqYqFy9H1N z1BiH~q@=XcVQXuf-uw1jN+;X?-`}$`z*)Pg26J1rufgfp`mh}}dVYS@uRp4~=dyW3 zO-<*G*UdluG6`UFGuB`Bm@K&aM5f-*#_0a=;DiBqbs10Z@XV2#g$y^MCVv*9x#al? zYfz0dB3FNO48^rVttbJzYaPKWa~K#H2M;|Wy_SwiESnr9JbT@%_hN7FcLHl3XUMIa z1@4!PDh*$~!+38^POBAHIndA0h%Wf_6-J0m;80_M1_6EgN^flt?`D0y4clSz*yC-Q z7?VPCYdP%YYoV#hR5iwnI&j6Nt*#b)t%Dwbve9TLeuw_b-MvbuYeSo1xbev1U>gVzhuZ7nv3D$#MYoM}N95|nzYp@? zpDr679@h1qen`*AaDv^<(lx8-J`pNj{(DT(y$~s=)}&h7XyXlWt-V{l`FqgiL!N-V zP|HD@MjT#h1jDn)a!$9C^cgF$M;3zWL66_`J$Z_2g;#*Np&a$hX-@oMmNh&OW{I?wIT{E1e$W*fBdXtl6x2MV! zZ8w=(;oZi$P%Z|B&V9jf6*z?QorR>SA>);!F$T=V5Uj0=X z#>SA$sz}*bwjA%0`a;Ehep8!;E;{9C;5_F|H#UZZAUEH==*#>dxA(Czv3czfq&@&p z(t0x*8F6&5WADV}NT{fgR-M%m6KmV0I8-e6>%)ZZAOiNa1F@4eXAzt;(y)dgRXjqm z?B5YjM=Nm{ZF>z14G1*^vd49Oc5!k->N_OA*edz4XXA}o+g#h9 z!IC3zFW=(F>V9h_+andib`^5U`^j;?rv_@yGP_!9E7NlZL^AijDUqk%b1DwKJ|xXq zr%4G2&g0G{##KRezBxKFZ6}xJ&siF`7G+LkdPpTiz1c-t+q4Y!r-ESI9SO-)SiiQ? zb>b2wbRy9pxmCh_{u|+9TeD3%AzEBjox&PedouSl=r%cfE1X`7{&ev^5T@@loszn1 z!Y|!+;8xsj779(`GzS`@QB*SOjKTw_<&7MS(pBnsqk6DIv>278b zBX;n=gBzJC4L&_T^`1?y^L5xiki_~iM1M__advfV5~YF@c5$MOV={WQY&CI~v*{D} zHmQ@8&aj%V7CHHc?suj$R$C}q3k^-w17|vH*D{w`_O1sGz}Ks?`P!+6DYgOR89pWQ z^N&>pRsU@xRB}5`f4{XDli&(i_XWrH=;-k1MK-|CvVWOeXVBkG+YEI^HPI4s_p|f( zIjrSwvqN;cxAl}~LA-~Tr(nd{#KB{hr0d5gL$@z<4ZNbY(L!F2IFPu@HL0x5h zl1)7FuaN+%$zjS@xtzWQ1YE5HEfcZ0Cc2z+Z|J+MR)_9uZE>bSu` z{FKZd)C{E&t~tsq#@2MfzVe3Fp!dEFIG;C&PBR)oM$|Z6CQ=i&JX@6c1qInpX_&MF z$)F{%h_K69$7iKITQkRphr%vB`TRHM(6h}1O`P&;iyTyvdv~E&kS5I5bAjb_r__bO zCm|MDHH0-#lhL;XkvuI)j!>($BDhI1;EXSM z-c~S$#F21iH?S|3t2neVyhDxg^7PrkrK;LVORPr2Zww*~7((_-!*J4JOPLC#@?@Y`Z4i(-XYT4|?hhtX= zCp@}3tBch_pC%9e|G3b#bAJrHVmJ=%`=-lZVI-`gX-pH9%YoV~>SlYHd^r1Unu!+1 z`fm0cn|OecLLP!XegS#bkXO^I@ste>AYNH=v?i)lups5PZoGG!XK;1GmcId?#B4$+ z<>vhfU!R3!6LdPgbX!Z+)>y{0E{hmL1B~aX&!_&CT|fV53cv{reBWZH{?NkT@klwP zT2=gVk;T_)6=~Ly)0-h7T(a>YOhRtb+fi$SkeVCGyU)-?7szFmTUalx90qZGQS!?$ zjpeDOWAhah&45F_eHG0{9X}xbL#>C(jUIA z=S#XkZqJdl(93JFXZUyg003nhKu$W@SNQgO}Cm10^_2U~Hm5TDfHFZk2hv;3!KqfBkm@risV*zvj2ZfPGsj z$U0}ZiJ$er7X3T=JA@UFlyv1(McI-RJAuK67x>-SOL;$(#5;iR*I()U+w}iwseAnP zoVr)%H1aMAn)4>c$7Z^@n_;sHS!|bk!+P3gJ{@)|fc>lg(X?E}ufGmE@j zd_iEBsU8c|;JbpAJQw1Hs0vDsif~&48Y?23}mr4~3 zZ&Lbj=~^z_5|3(SB}zR>{5<*j0~uMwD`sLrBaecz_5;Fa&211AWBxxE%^i*qnJh$7?Yvxtc z7SVYr3_*`yCqj89LAyUOC)mk(WlWbX=b0<;?rHxmOoacN>>Tz~KvgULWySn>D3|je jSn+=