顯示具有 Tech. Articles 標籤的文章。 顯示所有文章
顯示具有 Tech. Articles 標籤的文章。 顯示所有文章

2021年12月15日 星期三

Building a web site by Codeigniter 4 (CI4)

    I recently tried to build up a website by Codeigniter4 (known as CI4) and thought it is a pretty good framework.
    There are many tutorials and docs on the internet, so I skipped those things in this article. I'll probably focus on the issues I met during website development. 

Tools list
  • XDebug (Local debug)
  • VS Code with plugin 'PHP Debug' (Editor)
  • XAMPP (Local server)
  • Composer (Codeigniter4 and other comonents)
  • Bootstrap 5 (UI components)
Issues: 
  1. Public tool functions
    I write some tool in the folder 'Helpers' in order to use them accross all the controllers. I'm not sure it is the right way to do that.
  2. Modals in Bootstrap 5
    This is very usefull UI and easy to use except for one condition which is switching between modals. In my case, I need to switch modals according to what stage it is now. Unlikely, the POST data will be cleaned after modal switching. For now, I don't have a solution.
    Instead, I use POST method in very modal and check what stage is now and show the related modal by using javascript.
  3. Authentication check
    I write some authenticaiton check in the folder 'Filters' in order to check if the user has the permission to use the routing (page). In general, it is very convient. But, in some cases, the logic would be complicated if the page has multiple modes. Perhaps, it is due to two type users in my case. One is authenticated by other web site and the other is authenticated by my own web site.
  4. Complex Routing
    I'm still working on organizing the routing strategy. I have amost 50 routing pathes in my route definitions and it is not friendly readable. It is a very minor issue but need to fix it in the future.

2021年12月11日 星期六

Building Android/iOS app from a web site

Recently, I need to build an Android app and an iOS app for a website in a short time, so I choose the Convertify service.  Honestly, it's easy and costs a little money overall. But I still recommend it still needs time and programming skills to do that.


Step1: Buy the apps

Step2: Download source code from the link given in the mail

Step3: Set development environment (Android on windows/iOS on Mac OS)

Step4: Open Android/iOS workspace file (DO NOT open project file, building error might occur sometimes)

Step5: Build Bundled AAB file (Use the key store they provided or create it on your own) / Archive generic device (including upload procedure)

Step6: Upload your AAB file to your Google Play Console / Select your version on App Store Connect (It might take a while after you archived it in Step5.)

Step7: Complete the forms and submit them

Notes:
    After you complete the above steps, you can start to customize your Android/iOS apps such as app name, URL, icon, version number and etc. Why? Because everything you do will affect the building/uploading/archiving failure if you were rookies just like me.

P.S. Convertify support is not very strong. If you ran into some problems, they would recommend you to use "publy.app" service to upload the app for you. Of cause, you need to pay for it.

2016年6月15日 星期三

[C#] Upload/Update/Check/Delete/List Video with YouTube API

找了很多關於C#上使用YouTube API的資料後,拼拼湊湊出簡易的YouTube Video處理的程式

以下不說明如何匯入YouTube API Lib與其使用申請,網路上很多,可自行參考。

基本的建立UserCredential,並宣告YouTubeService,每個動作都是需要此步驟

UserCredential credential;
using( var stream = new FileStream(System.IO.Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "client_secrets.json"), FileMode.Open, FileAccess.Read) )
{
    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
        GoogleClientSecrets.Load(stream).Secrets,
        new[] { YouTubeService.Scope.Youtube, YouTubeService.Scope.YoutubeUpload },
        "user",
        CancellationToken.None,
        new FileDataStore(System.IO.Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "YoutubeCredential"))
    );
}
youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
    HttpClientInitializer = credential,
    ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
     

Upload
上傳單一影片檔案
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = videoName;
video.Snippet.Description = videoName;
video.Snippet.Tags = new string[] { "LowRes", processingTag };
video.Snippet.CategoryId = conf.configMapper.ey_sec.ey_youtube_category;//"22"; See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = conf.configMapper.ey_sec.ey_youtube_privacy; //"unlisted" or "private" or "public"
var filePath = target; // Replace with path to actual movie file.
try
{
    using( var fileStream = new FileStream(filePath, FileMode.Open) )
    {
        var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
        const int KB = 0x400;
        var minimumChunkSize = 256 * KB;
        videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
        videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
        videosInsertRequest.ChunkSize = minimumChunkSize * 32;
        await videosInsertRequest.UploadAsync();      
    }
}
catch(Exception ex)
{
    logtext.Out("[YouTube Exception] " + ex.Message + ex.StackTrace);
}

Check&Update
檢查VideoID是否有效並檢查是否為Duplicate video,並更新Tag資訊
var my_video_request = youtubeService.Videos.List("snippet, status");
my_video_request.Id = oldVideoID; // the Youtube video id of the video you want to update
my_video_request.MaxResults = 1;
var my_video_response = await my_video_request.ExecuteAsync();
if( my_video_response.Items.Count == 0 )
{
    return;
}
var video = my_video_response.Items[0];
video.Snippet.Tags.Add(oldVersionTag);
if( video.Status.UploadStatus == "rejected" )
{
    logtext.Out("[YouTube Info] Video upload status : " + video.Status.UploadStatus);
    logtext.Out("[YouTube Info] Video upload reject reason : " + video.Status.RejectionReason);
}
// and tell the changes we want to youtube
var my_update_request = youtubeService.Videos.Update(video, "snippet, status");
my_update_request.Execute();


Delete
刪除特定VideoID之影片
try
{
    var my_video_request = youtubeService.Videos.List("snippet, status");
    my_video_request.Id = oldVideoID; // the Youtube video id of the video you want to update
    my_video_request.MaxResults = 1;
    var my_video_response = await my_video_request.ExecuteAsync();
    if( my_video_response.Items.Count != 0 )
    {
        logtext.Out("[Info ] Old VideoID [" + oldVideoID + "] is valid, then delete it.");
        var my_update_request = youtubeService.Videos.Delete(oldVideoID);
        my_update_request.Execute();
    }
}
catch(Exception ex)
{
    logtext.Out("[YouTube Exception] " + ex.Message + ex.StackTrace);
}

List
列出所有YouTube上的所有Video ID
try
{
    var channelsListRequest = youtubeService.Channels.List("contentDetails");
    channelsListRequest.Mine = true;
    var channelsListResponse = channelsListRequest.Execute();
    foreach( var channel in channelsListResponse.Items )
    {
        var uploadsListId = channel.ContentDetails.RelatedPlaylists.Uploads;
        var nextPageToken = "";
        while( nextPageToken != null )
        {
            var playlistItemsListRequest = youtubeService.PlaylistItems.List("snippet");
            playlistItemsListRequest.PlaylistId = uploadsListId;
            playlistItemsListRequest.MaxResults = 50;
            playlistItemsListRequest.PageToken = nextPageToken;
            var playlistItemsListResponse = playlistItemsListRequest.Execute();
            foreach( var playlistItem in playlistItemsListResponse.Items )
            {
                string id = playlistItem.Snippet.ResourceId.VideoId;
                videoIDList.Add(id);
            }
            nextPageToken = playlistItemsListResponse.NextPageToken;
        }
    }
}
catch( Exception ex )
{
    logtext.Out("[Exception] " + ex.Message + ex.StackTrace);
}

[C#] File access issue

為了確保檔案已無任何程式存取,個人使用了下列方式

檢查檔案的A C M time,確保其時間與當下時間差大於指定的秒數

public static bool CheckFileInUse(Logs logtext, string filePath, int timeout)
{
    if( !EFCS.FileExist(logtext, filePath, Conf.RUNNING_NO_CMD_MESSAGE) )
        return false;
    DateTime lastAT = System.IO.File.GetLastAccessTime(filePath);
    DateTime createT = System.IO.File.GetCreationTime(filePath);
    DateTime lastWT = System.IO.File.GetLastWriteTime(filePath);
    DateTime nowT = DateTime.Now;
    if( (nowT - lastAT).TotalSeconds < 0 ||
        (nowT - createT).TotalSeconds < 0 ||
        (nowT - lastWT).TotalSeconds < 0 )
    {
        System.IO.File.SetCreationTime(filePath, DateTime.Now);
        System.IO.File.SetLastAccessTime(filePath, DateTime.Now);
        System.IO.File.SetLastWriteTime(filePath, DateTime.Now);
    }
    lastAT = System.IO.File.GetLastAccessTime(filePath);
    createT = System.IO.File.GetCreationTime(filePath);
    lastWT = System.IO.File.GetLastWriteTime(filePath);
    nowT = DateTime.Now;
    if( (nowT - lastAT).TotalSeconds < timeout ||
        (nowT - createT).TotalSeconds < timeout ||
        (nowT - lastWT).TotalSeconds < timeout )
    {
        logtext.Out("[Info ] [" + filePath + "] creation/modification time interval < " + timeout + " secs");
        return true;
    }
    return false;
}
此外,另增加了File Lock檢查

public static bool CheckLock(Logs logtext, string _strSourceFileName)
{
    int i = 0;
    bool bResult = true;
    while( i < 20 )//Retry 20次
    {
        try
        {
            using( Stream stream = System.IO.File.Open(_strSourceFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite) )
            {
                if( stream != null )
                {
                    logtext.Out("[Info ] [" + _strSourceFileName + "] is ready.");
                    bResult = false;
                    break;
                }
            }
        }
        catch( FileNotFoundException ex )
        {
            logtext.Out("[Info ] [" + _strSourceFileName + "] is not ready. (" + ex.Message + ")");
            bResult = true;
        }
        catch( IOException ex )
        {
            logtext.Out("[Info ] [" + _strSourceFileName + "] is not ready. (" + ex.Message + ")");
            bResult = true;
        }
        catch( UnauthorizedAccessException ex )
        {
            logtext.Out("[Info ] [" + _strSourceFileName + "] is not ready. (" + ex.Message + ")");
            bResult = true;
        }
        finally
        {
            i++;
            System.Threading.Thread.Sleep(500);
        }
    }
    return bResult;
}

2014年8月7日 星期四

設定 Powershell Codepage (字碼頁) 為UTF-8

設定 Powershell Codepage (字碼頁) 為UTF-8

打開Powershell,並輸入

chcp 65001

然後在視窗上按右鍵,選擇內容

在字型頁面中,選擇適當的自行後,按確定即可

2013年9月12日 星期四

[分享] Trinity Vengeance RaptorXL V3.3 for SONY xperia SL lt26ii 刷機

XDA 原文

小弟是新手~ 使用的是Xperia SL lt26ii(已解鎖),這一陣子嘗試了多款ROMs之後,像是  LeOpArDx RoM V3.1, pac 4.2.2 pure new,
JellySlim_FullPower**evo3, AOSPXXX等等,個人喜歡此版的ROM "Trinity Vengeance RaptorXL V3.3"

其間也相對刷過不少Kernels,像是Trinity Kernel, DooMLord等等~ 呵呵幾乎是亂刷一通~
最還是喜歡 Forzzaforilao的Kernel(超頻版的1.9GHz),使用很上穩定。

但目前因為Trinity Vengeance RaptorXL V3.3是based on 96 kernel,所以Forzzaforilao版本Kernel還要再等一陣子。

會推薦此ROM的原因:

  1. 穩定
  2. 操作順暢
  3. 反應及時
  4. 移除了許多內建程式
  5. 在我安裝了近100 apps後,系統與軟體能夠順暢操作與切換

分享目前使用狀況(一些我比較在意的部分及我在嘗試其他ROM時會碰到的狀況):

  1. 相機功能正常
  2. 一般使用無FC狀況發生
  3. 聽音樂無lag或中斷發生
  4. 系統操作沒有lag狀況發生
  5. 無app使用衝突
  6. 無異常Reboot


Notices
==========================下載連結請至XDA原文下載
刷之前請多多看原文說明或發問以減少問題的發生~
檔案大小約1GB,裡面包含了兩種核心、多種常用apps、Google apps、Launchers供選擇預先安裝。

p.s. 若有需要讓google app自動還原安裝,記得刷完後,把3G關掉、Wi-Fi打開、然後先至"設定"->"Feature Control"->"Tool" 頁面中,修改"Device Hostname"

android-xxxxxxxxxxxxxxxx

"xxxxxxxxxxxxxxxxv"修改為原本的Android ID,修改好後重新開機,然後再連上Wi-Fi並登入你的Google帳號即可

個人建議解鎖後自由度比較高,可以選擇自己想要的kernel。

如果,最後真的真的很不幸的開不了機,頂多麻煩一點重刷官方ftf檔就好了。



p.s. 我的習慣是手機都會留一份可以work的ROM,以備想要嘗試的ROM有問題開不了時,可以立刻刷回可用的ROM,然後重新放別的ROM試。這樣就不用強刷回官方的ROM,在去搞Root、CWM等等的東西。

設定>Xperia>網際網路設定
要先下載網際網路跟MMS設定才能上網

6.0以上的CWM是開機亮紫燈按音量+
==========================


Kernel
==========================
DooMKernel v06出了,有實驗精神的可以嘗試 (我目前一樣會等Forzzaforilao的OC版本)
=======
注意!!!  此Kernel,我未試過,指示提供資訊給大家!!
[url=http://forum.xda-developers.com/showthread.php?t=2305591]DooMKernel JB v06[/url]
==========================


我刷機的動作 (供參考)
==========================

1. wipe data/factory reset
2. wipe cache partition
3. advanced -> wipe delvik cache
4. advanced -> fix permissions
5. mounts and storage -> format /system
6. mounts and storage -> format /cache
7. mounts and storage -> format /data
8. install zip from sdcard -> choose zip from sdcard -> Trinity.....

開始進入選單......
其中Kernel的選擇頁面

Select Your Favorite Kernel Flavor.

我是選
Advance Stock kernel 96Source
1080p Lagfix With latest 96Kernel FW

reboot....

p.s.裡面的內建軟體,我個人是能不勾選就不勾選。
==========================

螢幕截圖
==========================









2012年11月20日 星期二

[VAIO SZ58 WebCam Driver] Sony Visual Communication Camera VGP-VCC7 Win 7 64bit drivers


在SONY VAIO SZ58 上安裝Win 7 64-bit時,發生WebCam無法啟動,所以找了一下driver,終於找到合用的,如下



How to use:
1.) In device managerupdate the driver for the Sony Webcam, 'browse my computer...'. 
2.) Now select 'LET ME PICK FROM A LIST.....'.
3.) Click 'HAVE DISKand then browse to the path of the extracted zip file.
4.) 'Sony Visual Communication Camera VGP-VCC7is now selectedClick NEXT.
5.) Windows can't verify publisher.... who cares. 'INSTALL THIS DRIVER SOFTWAREANYWAY'
6.) Test in Skype

2012年5月23日 星期三

使用WireShark 抓取SOAP封包

Filter輸入ip.addr==A&&ip.addr==B
A:為你Client之IP
B:為你Server之IP

會filter出Client與Server間的封包,大部分為SOAP。選擇其一封包並於其上按右鍵,選擇 "Follow TCP Stream",此時就會跳出一個視窗,show出所有的封包內容,就可以一次看到所有SOAP的XML資料。

Done

2012年5月22日 星期二

[SOAP] No address associated with

PHP SOAP在Load wsdl時發生的錯誤
Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: No address associated with 
相關討論串Non-SOAP bindingBug#50443

解決方法為移除無Address的port之Binding,如下紅色部分。

Example:


  
    https://services.contentdirect.tv/v3.0/SubscriberService.svc"/>
  
  



2012年5月18日 星期五

VAIO 顯卡更新成官網最新driver方法

最近想要把我的VAIO sz58的顯示卡驅動程式更新到最新時,發現nVidia官方driver無法安裝,所以google了一下解決方法



依照以下步驟:
1. 下載對應到官方網站最新nVidia驅動程式版本,先執行一次,他會把安裝軟體解壓縮到C槽。
2. 到下面路徑找到兩個檔案 nvaa.inf, nvac.inf (版本號296.10會不一定會一樣)

C:\NVIDIA\DisplayDriver\296.10\WinVista_Win7\International\Display.Driver\

3. 修改裡面的內容 (記得先備份),每個檔案有3個sections要改,有兩個是緊接著[Manufacturer]這個section之後的兩個sections,另一個是[Strings]這個section,要加的內容都加在所屬section的最後面。 (紅色字體為要加的內容)

另外特別注意的是"PCI\VEN_10DE&DEV_0427&SUBSYS_9008104D" 此為你的顯卡硬體裝置ID,可在Device manager裡查看,如下圖



nvaa.inf
...

[Manufacturer]
%NVIDIA_A% = NVIDIA_SetA_Devices,NTx86.6.0,NTx86.6.1

[NVIDIA_SetA_Devices.NTx86.6.0]

%NVIDIA_DEV.0407.0770.152D% = Section001, PCI\VEN_10DE&DEV_0407&SUBSYS_0770152D 
%NVIDIA_DEV.0407.1515.1043% = Section001, PCI\VEN_10DE&DEV_0407&SUBSYS_15151043 
...
%NVIDIA_DEV.0427.01% = Section001, PCI\VEN_10DE&DEV_0427&SUBSYS_9008104D


[NVIDIA_SetA_Devices.NTx86.6.1]
%NVIDIA_DEV.0407.0770.152D% = Section002, PCI\VEN_10DE&DEV_0407&SUBSYS_0770152D 
%NVIDIA_DEV.0407.1515.1043% = Section002, PCI\VEN_10DE&DEV_0407&SUBSYS_15151043 
...
%NVIDIA_DEV.0427.01% = Section002, PCI\VEN_10DE&DEV_0427&SUBSYS_9008104D


[Strings]
DiskID1 = "NVIDIA Windows Vista / Windows 7 (32 bit) Driver Library Installation Disk 1"
NVIDIA = "NVIDIA"
NVIDIA_A = "NVIDIA"
NVIDIA_DEV.0407.0770.152D = "NVIDIA GeForce 8600M GT"
NVIDIA_DEV.0407.1515.1043 = "NVIDIA GeForce 8600M GT "
...
NVIDIA_DEV.0427.01 = "NVIDIA GeForce 8400M GS"

nvac.inf
...


[Manufacturer]
%NVIDIA_A% = NVIDIA_SetA_Devices,NTx86.6.0,NTx86.6.1

[NVIDIA_SetA_Devices.NTx86.6.0]
%NVIDIA_DEV.0405.011D.1025% = Section001, PCI\VEN_10DE&DEV_0405&SUBSYS_011D1025 
%NVIDIA_DEV.0405.011E.1025% = Section001, PCI\VEN_10DE&DEV_0405&SUBSYS_011E1025 
...
%NVIDIA_DEV.0427.01% = Section001, PCI\VEN_10DE&DEV_0427&SUBSYS_9008104D

[NVIDIA_SetA_Devices.NTx86.6.1]
%NVIDIA_DEV.0405.011D.1025% = Section002, PCI\VEN_10DE&DEV_0405&SUBSYS_011D1025 
%NVIDIA_DEV.0405.011E.1025% = Section002, PCI\VEN_10DE&DEV_0405&SUBSYS_011E1025 
...
%NVIDIA_DEV.0427.01% = Section002, PCI\VEN_10DE&DEV_0427&SUBSYS_9008104D

[Strings]
DiskID1 = "NVIDIA Windows Vista / Windows 7 (32 bit) Driver Library Installation Disk 1"
NVIDIA = "NVIDIA"
NVIDIA_A = "NVIDIA"
NVIDIA_DEV.0405.011D.1025 = "NVIDIA GeForce 9500M GS"
NVIDIA_DEV.0405.011E.1025 = "NVIDIA GeForce 9500M GS "
...
NVIDIA_DEV.0427.01 = "NVIDIA GeForce 8400M GS"

4. 修改完後,存檔,重新執行setup.exe,此執行檔會在下面這個目錄
C:\NVIDIA\DisplayDriver\296.10\WinVista_Win7\International
5. 重開機

Done

2011年12月13日 星期二

[轉貼] Oracle SQL Developer Tutorial ((Useful~

Vincent Wang已在Google+和你分享訊息。 Google+ 讓你在網路上體驗有如現實生活般的分享樂趣。 瞭解詳情
加入 Google+
這是系統通知訊息:Vincent Wang與uniquesky1.annie@blogger.com分享了這個項目。 取消訂閱這些電子郵件。

2011年7月28日 星期四

How to add lib to your MSVS

Add library to visual studio 2008 project

設置的地方在此

Project Properties / Configuration Properties / Linker / Input / Additional Dependencies
(專案>屬性>組態屬性>連結器>輸入>其它相依性)





加入所要加入的lib,如下
"c:\visa\visa32.lib"
及可!

2011年7月21日 星期四

Android requires .class compatibility set to 5.0

解決方法: 

1:
選擇 project -> Android Tools -> Fix Project Properties. 
重新 clean project F5刷新工程


2011年4月8日 星期五

BCB TStringGrid之ScrollBar自動捲動方式

在BCB中若要使TStringGrid程式控制上下捲動,我在網路上找了很久,可用下列function來達成

SendMessage(TStringGrid->Handle, WM_VSCROLL, SB_LINEDOWN, 0);

主要可變動的參數式SB_LINEDOWN,有以下選擇



SB_BOTTOM 
  Scrolls   to   the   lower   right. 
SB_ENDSCROLL 
  Ends   scroll. 
SB_LINEDOWN 
  Scrolls   one   line   down. 
SB_LINEUP 
  Scrolls   one   line   up. 
SB_PAGEDOWN 
  Scrolls   one   page   down. 
SB_PAGEUP 
  Scrolls   one   page   up. 
SB_THUMBPOSITION 
  The   user   has   dragged   the   scroll   box   (thumb)   and   released   the   mouse   button.   The   high-order   word   indicates   the   position   of   the   scroll   box   at   the   end   of   the   drag   operation. 
SB_THUMBTRACK 
  The   user   is   dragging   the   scroll   box.   This   message   is   sent   repeatedly   until   the   user   releases   the   mouse   button.   The   high-order   word   indicates   the   position   that   the   scroll   box   has   been   dragged   to. 
SB_TOP 
  Scrolls   to   the   upper   left.

2011年4月6日 星期三

在BCB中之ComPort 設定

來源網址 

找了很久才找到的解法~

在BCB 6.0中,若使用ComPort 4.11,設定Port時所遇到的問題。
錯誤訊息大致如下

unresolved external ...fastcall SetPortA...

而錯誤的程式碼如下

ComPort->Port = "COM8";

解決方式就是修改
C:\Program Files\Borland\CBuilder6\Include\winspool.h

在此檔案找到下面內容


BOOL

WINAPI

SetPortA(

IN LPSTR     pName,

IN LPSTR     pPortName,

IN DWORD       dwLevel,

IN LPBYTE      pPortInfo

);

BOOL

WINAPI

SetPortW(

IN LPWSTR     pName,

IN LPWSTR     pPortName,

IN DWORD       dwLevel,

IN LPBYTE      pPortInfo

);

#ifdef UNICODE

#define SetPort  SetPortW

#else

#define SetPort  SetPortA

#endif // !UNICODE

然後在頭尾分別加上

#ifdef DONT_USE_WINSPOOL_SETPORTA


#endif //!DONT_USE_WINSPOOL_SETPORTA
成為

#ifdef DONT_USE_WINSPOOL_SETPORTA
SetPortA(
...
...
#endif // !UNICODE
#endif //!DONT_USE_WINSPOOL_SETPORTA
存檔後,重新compile即可。

搜尋此網誌