4 Commits

Author SHA1 Message Date
f73c45bb48 Target crosslang v0.0.10, add /api/v1/progress_ex.json
All checks were successful
Build and Deploy on Tag / 🔨 Build (push) Successful in 22s
2026-09-10 03:29:30 -05:00
dc270e6785 Target crosslang v0.0.10, add /api/v1/progress_ex.json
All checks were successful
Build and Deploy on Tag / 🔨 Build (push) Successful in 20s
2026-09-10 03:15:05 -05:00
456577f2f7 Fix for crosslang v0.0.10
Some checks failed
Build and Deploy on Tag / 🔨 Build (push) Has been cancelled
2026-09-02 12:32:59 -05:00
027f601887 Fix for crosslang v0.0.10 2026-09-02 12:31:31 -05:00
12 changed files with 140 additions and 89 deletions

View File

@@ -11,4 +11,4 @@ ENV TYTDDIR=/data
EXPOSE 3255 EXPOSE 3255
ENTRYPOINT ["crossvm","/app/tytd2025.crvm","--port=3255"] ENTRYPOINT ["crosslang","vm","/app/tytd2025.crvm","--port=3255"]

View File

@@ -1,5 +1,5 @@
# TYTD 2025 # TYTD 2025
My web based YouTube downloader that I created in 2025 writen in [CrossLang](https://crosslang.tesseslanguage.com/), my own language My web based YouTube downloader that I created in 2025 written in [CrossLang](https://crosslang.tesseslanguage.com/), my own language
![The Image](https://crosslang.tesseslanguage.com/images/tytdscreenshot.jpg) ![The Image](https://crosslang.tesseslanguage.com/images/tytdscreenshot.jpg)
@@ -14,8 +14,8 @@ My web based YouTube downloader that I created in 2025 writen in [CrossLang](htt
- Search and browse your downloaded videos, playlists, or channels (the search is very basic though) - Search and browse your downloaded videos, playlists, or channels (the search is very basic though)
- User accounts - User accounts
- Videos can be tagged based on your downloader's TYTD tag (to determine which instance downloaded it) - Videos can be tagged based on your downloader's TYTD tag (to determine which instance downloaded it)
- Plugins from [CPKG](https://cpkg.tesseslangauge.com/) or any CPKG compliant server - Plugins from [CPKG](https://cpkg.tesseslanguage.com/) or any CPKG compliant server
- Can download YouTube videos either Low quality (but doesn't) require [ffmpeg](https://ffmpeg.org/), you can also download individual streams (also doesn't need [ffmpeg](https://ffmpeg.org/)), or to MP4 (doesn't work on wii due to libx264 having illegal instruction), MKV (so no transcode), MP3 or FLAC (these do need [ffmpeg](https://ffmpeg.org/) in your PATH however) - Can download YouTube videos either Low quality (but doesn't) require [ffmpeg](https://ffmpeg.org/), MP3, FLAC or AMV (these do need [ffmpeg](https://ffmpeg.org/) in your PATH however)
- Runs on the Wii using the [Wii Linux Continuation Project](https://wiibrew.org/wiki/Wii-Linux#Wii_Linux_Continuation_Project) (albeit extremely slowly, despite this that's where I run it) - Runs on the Wii using the [Wii Linux Continuation Project](https://wiibrew.org/wiki/Wii-Linux#Wii_Linux_Continuation_Project) (albeit extremely slowly, despite this that's where I run it)
# What this project uses (attribution) # What this project uses (attribution)
@@ -37,7 +37,7 @@ Install [crosslang](https://crosslang.tesseslanguage.com/downloads/index.html)
from source: from source:
```bash ```bash
git clone https://onedev.site.tesses.net/tytd2025 git clone https://git.tesses.org/tesses50/tytd2025
cd tytd2025/Tesses.YouTubeDownloader.Server cd tytd2025/Tesses.YouTubeDownloader.Server
crosslang install-webapp crosslang install-webapp
``` ```

View File

@@ -28,6 +28,6 @@
"template_icon": "icon.png" "template_icon": "icon.png"
}, },
"name": "Tesses.YouTubeDownloader.PluginTemplate", "name": "Tesses.YouTubeDownloader.PluginTemplate",
"version": "1.0.0.0-dev", "version": "1.0.1.0-dev",
"icon": "icon.png" "icon": "icon.png"
} }

View File

@@ -14,6 +14,6 @@
"project_dependencies": [ "project_dependencies": [
"..\/Tesses.YouTubeDownloader" "..\/Tesses.YouTubeDownloader"
], ],
"version": "1.0.0.0-dev", "version": "1.0.1.0-dev",
"compTime": "secure" "compTime": "secure"
} }

View File

@@ -1,12 +1,12 @@
func Components.Progress(tytd) func Components.Progress(tytd)
{ {
var vid = tytd.CurrentVideo; var progress = tytd.GetProgress();
var html = <div hx-trigger="every 4500ms" hx-indicator="none" hx-get="./progress" hx-swap="outerHTML"> var html = <div hx-trigger="every 4500ms" hx-indicator="none" hx-get="./progress" hx-swap="outerHTML">
<h2><a hx-target="body" hx-push-url="true" hx-get={$"./watch?v={Net.Http.UrlEncode(vid.VideoId)}"} href={$"./watch?v={Net.Http.UrlEncode(vid.VideoId)}"}>{vid.Title}</a></h2> <h2><a hx-target="body" hx-push-url="true" hx-get={$"./watch?v={Net.Http.UrlEncode(progress.CurrentVideo.VideoId)}"} href={$"./watch?v={Net.Http.UrlEncode(progress.CurrentVideo.VideoId)}"}>{progress.CurrentVideo.Title}</a></h2>
<h4><a hx-target="body" hx-push-url="true" hx-get={$"./channel?id={Net.Http.UrlEncode(vid.ChannelId)}"} href={$"./channel?id={Net.Http.UrlEncode(vid.ChannelId)}"}>{vid.Channel}</a></h4> <h4><a hx-target="body" hx-push-url="true" hx-get={$"./channel?id={Net.Http.UrlEncode(progress.CurrentVideo.ChannelId)}"} href={$"./channel?id={Net.Http.UrlEncode(progress.CurrentVideo.ChannelId)}"}>{progress.CurrentVideo.Channel}</a></h4>
<progress class="wavy light-green-text" value={tytd.CurrentVideoProgress * 100.0} max="100" min="0"></progress> <progress class="wavy light-green-text" value={progress.CurrentVideoProgress * 100.0} max="100" min="0"></progress>
</div>; </div>;
return html; return html;
} }

View File

@@ -139,8 +139,8 @@ class TYTDApp {
{ {
var incorrect=false; var incorrect=false;
const redirect = ctx.QueryParams.TryGetFirst("redirect") ?? "/"; const redirect = ctx.QueryParams.TryGetFirst("redirect") ?? "/";
const username = ctx.QueryParams.TryGetFirst("username") ?? ""; const username = ctx.BodyParams.TryGetFirst("username") ?? "";
const password = ctx.QueryParams.TryGetFirst("password") ?? ""; const password = ctx.BodyParams.TryGetFirst("password") ?? "";
@@ -218,16 +218,16 @@ class TYTDApp {
{ {
if(ctx.Method == "POST") if(ctx.Method == "POST")
{ {
const from = ctx.QueryParams.TryGetFirstInt("from"); const from = ctx.BodyParams.TryGetFirstInt("from");
switch(from) switch(from)
{ {
case 1: case 1:
{ {
const enablePlugins = ctx.QueryParams.GetFirstBoolean("enablePlugins"); const enablePlugins = ctx.BodyParams.GetFirstBoolean("enablePlugins");
const tag = ctx.QueryParams.TryGetFirst("tag") ?? "UnknownPC"; const tag = ctx.BodyParams.TryGetFirst("tag") ?? "UnknownPC";
const pollHours = ctx.QueryParams.TryGetFirstInt("pollHours") ?? 3; const pollHours = ctx.BodyParams.TryGetFirstInt("pollHours") ?? 3;
const pollMinutes = ctx.QueryParams.TryGetFirstInt("pollMinutes") ?? 0; const pollMinutes = ctx.BodyParams.TryGetFirstInt("pollMinutes") ?? 0;
const pollSeconds = ctx.QueryParams.TryGetFirstInt("pollSeconds") ?? 0; const pollSeconds = ctx.BodyParams.TryGetFirstInt("pollSeconds") ?? 0;
this.OOBE_STATE.tag = tag; this.OOBE_STATE.tag = tag;
@@ -248,7 +248,7 @@ class TYTDApp {
case 3: case 3:
{ {
const createUser = (ctx.QueryParams.TryGetFirst("user") ?? "no")=="yes"; const createUser = (ctx.BodyParams.TryGetFirst("user") ?? "no")=="yes";
var seconds = this.OOBE_STATE.pollSeconds; var seconds = this.OOBE_STATE.pollSeconds;
seconds += this.OOBE_STATE.pollMinutes * 60; seconds += this.OOBE_STATE.pollMinutes * 60;
@@ -386,18 +386,38 @@ class TYTDApp {
} }
else if(ctx.Path == "/passwd") else if(ctx.Path == "/passwd")
{ {
var incorrect=false; var result = {success=true};
const password = ctx.QueryParams.TryGetFirst("password") ?? "";
const newpassword = ctx.QueryParams.TryGetFirst("newpassword") ?? "";
const confirm = ctx.QueryParams.TryGetFirst("newpassword") ?? ""; const redirect = ctx.BodyParams.TryGetFirst("redirect") ?? "/login";
const password = ctx.BodyParams.TryGetFirst("password") ?? "";
const newpassword = ctx.BodyParams.TryGetFirst("newpassword") ?? "";
const confirm = ctx.BodyParams.TryGetFirst("confirm") ?? "";
const logout = ctx.BodyParams.GetFirstBoolean("logout");
if(ctx.Method == "POST") if(ctx.Method == "POST")
{ {
this.TYTD.Passwd(); //public Passwd(ctx, oldPassword, newPassword, logout)
if(password != newpassword)
{
result = {success = false, reason = "Passwords do not match"};
}
else
result = this.TYTD.Passwd(ctx, password, newpassword, logout);
if(result.success)
{
ctx.StatusCode = 307;
ctx.SendRedirect(redirect);
return true;
} }
ctx.WithMimeType("text/html").SendText(Pages.ChangePassword(redirect, incorrect)); }
ctx.WithMimeType("text/html").SendText(Pages.ChangePassword(redirect, result.success ? null : result.reason));
return true;
} }
else if(ctx.Path == "/newuser") else if(ctx.Path == "/newuser")
@@ -405,14 +425,14 @@ class TYTDApp {
var error = null; var error = null;
if(ctx.Method == "POST") if(ctx.Method == "POST")
{ {
const username = ctx.QueryParams.TryGetFirst("username"); const username = ctx.BodyParams.TryGetFirst("username");
const password = ctx.QueryParams.TryGetFirst("password"); const password = ctx.BodyParams.TryGetFirst("password");
const confirm = ctx.QueryParams.TryGetFirst("confirm"); const confirm = ctx.BodyParams.TryGetFirst("confirm");
const isAdmin = ctx.QueryParams.GetFirstBoolean("isAdmin"); const isAdmin = ctx.BodyParams.GetFirstBoolean("isAdmin");
const canUsePlugins = ctx.QueryParams.GetFirstBoolean("canUsePlugins"); const canUsePlugins = ctx.BodyParams.GetFirstBoolean("canUsePlugins");
const canManagePlugins = ctx.QueryParams.GetFirstBoolean("canManagePlugins"); const canManagePlugins = ctx.BodyParams.GetFirstBoolean("canManagePlugins");
const canDownloadDB = ctx.QueryParams.GetFirstBoolean("canDownloadDB"); const canDownloadDB = ctx.BodyParams.GetFirstBoolean("canDownloadDB");
const redirect = ctx.QueryParams.TryGetFirst("redirect") ?? "/";
var flags = 0; var flags = 0;
if(isAdmin) flags |= UserFlags.AdminFlag; if(isAdmin) flags |= UserFlags.AdminFlag;
if(canUsePlugins) flags |= UserFlags.PluginFlag; if(canUsePlugins) flags |= UserFlags.PluginFlag;
@@ -434,12 +454,6 @@ class TYTDApp {
else { else {
error = this.TYTD.CreateAccount(ctx, username, password, flags); error = this.TYTD.CreateAccount(ctx, username, password, flags);
} }
if(!TypeIsString(error))
{
ctx.StatusCode=303;
ctx.SendRedirect(redirect);
}
} }
ctx.WithMimeType("text/html").SendText(Pages.NewUser(error)); ctx.WithMimeType("text/html").SendText(Pages.NewUser(error));
return true; return true;
@@ -526,14 +540,14 @@ class TYTDApp {
} }
return false; return false;
} }
else if(ctx.Path == "/api/v1/progress_ex.json")
{
ctx.WithMimeType("application/json").SendJson(this.TYTD.GetProgressEx());
return true;
}
else if(ctx.Path == "/api/v1/progress.json") else if(ctx.Path == "/api/v1/progress.json")
{ {
var jo = { ctx.WithMimeType("application/json").SendJson(this.TYTD.GetProgress());
CurrentVideoProgress = this.TYTD.CurrentVideoProgress,
CurrentVideo = this.TYTD.CurrentVideo
};
ctx.WithMimeType("application/json").SendJson(jo);
return true; return true;
} }
else if(ctx.Path == "/api/v1/add") else if(ctx.Path == "/api/v1/add")
@@ -790,7 +804,7 @@ class TYTDApp {
} }
else if(ctx.Method == "POST") else if(ctx.Method == "POST")
{ {
var description =ctx.QueryParams.TryGetFirst("description"); var description =ctx.BodyParams.TryGetFirst("description");
this.TYTD.SetPersonalListDescription(name,description); this.TYTD.SetPersonalListDescription(name,description);
ctx.WithMimeType("text/html").SendText(Components.PersonalListDescription(this.TYTD,name,false)); ctx.WithMimeType("text/html").SendText(Components.PersonalListDescription(this.TYTD,name,false));
@@ -848,10 +862,10 @@ class TYTDApp {
{ {
if(whoami.username != user) if(whoami.username != user)
{ {
const isAdmin = ctx.QueryParams.GetFirstBoolean("isAdmin"); const isAdmin = ctx.BodyParams.GetFirstBoolean("isAdmin");
const canUsePlugins = ctx.QueryParams.GetFirstBoolean("canUsePlugins"); const canUsePlugins = ctx.BodyParams.GetFirstBoolean("canUsePlugins");
const canManagePlugins = ctx.QueryParams.GetFirstBoolean("canManagePlugins"); const canManagePlugins = ctx.BodyParams.GetFirstBoolean("canManagePlugins");
const canDownloadDB = ctx.QueryParams.GetFirstBoolean("canDownloadDB"); const canDownloadDB = ctx.BodyParams.GetFirstBoolean("canDownloadDB");
var flags = 0; var flags = 0;
if(isAdmin) flags |= UserFlags.AdminFlag; if(isAdmin) flags |= UserFlags.AdminFlag;
if(canUsePlugins) flags |= UserFlags.PluginFlag; if(canUsePlugins) flags |= UserFlags.PluginFlag;
@@ -926,11 +940,11 @@ class TYTDApp {
tag tag
enablePlugins enablePlugins
*/ */
var enablePlugins = ctx.QueryParams.GetFirstBoolean("enablePlugins"); var enablePlugins = ctx.BodyParams.GetFirstBoolean("enablePlugins");
var tag = ctx.QueryParams.TryGetFirst("tag"); var tag = ctx.BodyParams.TryGetFirst("tag");
var hours = ctx.QueryParams.TryGetFirstInt("hours"); var hours = ctx.BodyParams.TryGetFirstInt("hours");
var minutes = ctx.QueryParams.TryGetFirstInt("minutes"); var minutes = ctx.BodyParams.TryGetFirstInt("minutes");
var seconds = ctx.QueryParams.TryGetFirstInt("seconds"); var seconds = ctx.BodyParams.TryGetFirstInt("seconds");
if(TypeOf(tag) == "String" && TypeOf(hours) == "Long" && TypeOf(minutes) == "Long" && TypeOf(seconds) == "Long") if(TypeOf(tag) == "String" && TypeOf(hours) == "Long" && TypeOf(minutes) == "Long" && TypeOf(seconds) == "Long")
{ {
@@ -953,32 +967,40 @@ class TYTDApp {
else if(ctx.Path == "/watch_videos") else if(ctx.Path == "/watch_videos")
{ {
const video_ids = ctx.QueryParams.TryGetFirst("video_ids");
if(TypeIsString(video_ids))
{
if(ctx.Method=="GET") if(ctx.Method=="GET")
{
const video_ids = ctx.QueryParams.TryGetFirst("video_ids");
if(TypeIsString(video_ids))
{ {
ctx.WithMimeType("text/html").SendText(Pages.YouTubeAnonyPlaylist(video_ids)); ctx.WithMimeType("text/html").SendText(Pages.YouTubeAnonyPlaylist(video_ids));
return true; return true;
} }
}
if(ctx.Method=="POST") if(ctx.Method=="POST")
{ {
const name = ctx.QueryParams.TryGetFirst("name"); const video_ids = ctx.BodyParams.TryGetFirst("video_ids");
if(TypeIsString(video_ids))
{
const name = ctx.BodyParams.TryGetFirst("name");
if(TypeIsString(name)) if(TypeIsString(name))
{ {
const nameParts=video_ids.Split(","); const nameParts=video_ids.Split(",");
Console.WriteLine(nameParts); //Console.WriteLine(nameParts);
each(var item : nameParts) each(var item : nameParts)
{ {
this.TYTD.AddToPersonalList(name,item); this.TYTD.AddToPersonalList(name,item);
} }
Console.WriteLine(name); //Console.WriteLine(name);
ctx.SendRedirect($"/list?name={Net.Http.UrlEncode(name)}",303); ctx.SendRedirect($"/list?name={Net.Http.UrlEncode(name)}",303);
return true; return true;
} }
} }
} }
} }
else if(ctx.Path == "/playlist") else if(ctx.Path == "/playlist")
{ {
@@ -992,9 +1014,9 @@ class TYTDApp {
} }
else if(ctx.Path == "/add") else if(ctx.Path == "/add")
{ {
var url = ctx.QueryParams.TryGetFirst("url"); var url = ctx.BodyParams.TryGetFirst("url");
var action =ctx.QueryParams.TryGetFirst("action"); var action =ctx.BodyParams.TryGetFirst("action");
var res = ctx.QueryParams.TryGetFirst("res"); var res = ctx.BodyParams.TryGetFirst("res");
if(action == "add") if(action == "add")
{ {
this.TYTD.DownloadItem(url,res); this.TYTD.DownloadItem(url,res);
@@ -1022,9 +1044,9 @@ class TYTDApp {
} }
else if(ctx.Path == "/add-video") else if(ctx.Path == "/add-video")
{ {
var url = ctx.QueryParams.TryGetFirst("url"); var url = ctx.BodyParams.TryGetFirst("url");
var action =ctx.QueryParams.TryGetFirst("action"); var action =ctx.BodyParams.TryGetFirst("action");
var res = ctx.QueryParams.TryGetFirst("res"); var res = ctx.BodyParams.TryGetFirst("res");
if(action == "add") if(action == "add")
{ {
this.TYTD.DownloadItem(url,res); this.TYTD.DownloadItem(url,res);
@@ -1045,10 +1067,10 @@ class TYTDApp {
} }
else if(ctx.Path == "/add-to-list") else if(ctx.Path == "/add-to-list")
{ {
var id = ctx.QueryParams.TryGetFirst("id"); var id = ctx.BodyParams.TryGetFirst("id");
if(TypeOf(id) != "String") id = ""; if(TypeOf(id) != "String") id = "";
var name = ctx.QueryParams.TryGetFirst("name"); var name = ctx.BodyParams.TryGetFirst("name");
if(TypeOf(name) != "String") name = ""; if(TypeOf(name) != "String") name = "";
if(name == "") if(name == "")
@@ -1089,9 +1111,9 @@ class TYTDApp {
{ {
if(!UserFlags.CanManagePlugins(this.TYTD.IsLoggedIn(ctx))) return false; if(!UserFlags.CanManagePlugins(this.TYTD.IsLoggedIn(ctx))) return false;
var data = { var data = {
name = ctx.QueryParams.TryGetFirst("name"), name = ctx.BodyParams.TryGetFirst("name"),
version = ctx.QueryParams.TryGetFirst("version"), version = ctx.BodyParams.TryGetFirst("version"),
action = ctx.QueryParams.TryGetFirst("action") action = ctx.BodyParams.TryGetFirst("action")
}; };
var mustConfirm = false; var mustConfirm = false;

View File

@@ -1,14 +1,14 @@
func Pages.ChangePassword(redirect,incorrect) func Pages.ChangePassword(redirect,error)
{ {
const html = <null> const html = <null>
<if(incorrect)> <if(TypeIsString(error))>
<true> <true>
<blockquote> <blockquote>
<h5>The password could not be changed</h5> <h5>{error}</h5>
</blockquote> </blockquote>
</true> </true>
</if> </if>

View File

@@ -51,7 +51,7 @@ func Pages.OobePage2(tytd,ctx)
<div class="small-margin">Security</div> <div class="small-margin">Security</div>
</div> </div>
</nav> </nav>
<form hx-get="./oobe" hx-target="body" hx-push-url="true"> <form hx-post="./oobe" hx-target="body" hx-push-url="true">
<input type="hidden" name="page" value="2"> <input type="hidden" name="page" value="2">
<div class="field suffix border round"> <div class="field suffix border round">
<select name="server"> <select name="server">

View File

@@ -14,6 +14,6 @@
} }
], ],
"name": "Tesses.YouTubeDownloader", "name": "Tesses.YouTubeDownloader",
"version": "1.0.0.0-dev", "version": "1.0.1.0-dev",
"icon": "icon.png" "icon": "icon.png"
} }

View File

@@ -525,26 +525,21 @@ class TYTD.Downloader {
{ {
case Resolution.LowVideo: case Resolution.LowVideo:
return dir / "ytmux.mp4"; return dir / "ytmux.mp4";
break;
case Resolution.VideoOnly: case Resolution.VideoOnly:
return dir / "vo.bin"; return dir / "vo.bin";
break;
case Resolution.AudioOnly: case Resolution.AudioOnly:
return dir / "ao.bin"; return dir / "ao.bin";
break;
case Resolution.MP4: case Resolution.MP4:
return dir / "conv.mp4"; return dir / "conv.mp4";
break;
case Resolution.MKV: case Resolution.MKV:
return dir / "conv.mkv"; return dir / "conv.mkv";
break;
case Resolution.MP3: case Resolution.MP3:
return dir / "conv.mp3"; return dir / "conv.mp3";
break;
case Resolution.FLAC: case Resolution.FLAC:
return dir / "conv.flac"; return dir / "conv.flac";
break; case Resolution.AMV:
return dir / "conv.amv";
} }
throw $"Could not get file path for format {res}"; throw $"Could not get file path for format {res}";
} }
@@ -849,14 +844,14 @@ class TYTD.Downloader {
public BeforeQueued = new TYTD.Event(); public BeforeQueued = new TYTD.Event();
public CurrentVideo = { private CurrentVideo = {
Title = "N/A", Title = "N/A",
Channel = "N/A", Channel = "N/A",
VideoId = "", VideoId = "",
ChannelId = "" ChannelId = ""
}; };
public CurrentVideoProgress = 0.0; private CurrentVideoProgress = 0.0;
public VideoEnded = new TYTD.Event(); public VideoEnded = new TYTD.Event();
@@ -874,6 +869,30 @@ class TYTD.Downloader {
private DownloaderThreadHandle; private DownloaderThreadHandle;
public GetProgress() {
this.Mutex.Lock();
const progress = {CurrentVideoProgress,CurrentVideo };
this.Mutex.Unlock();
return progress;
}
public GetProgressEx() {
this.Mutex.Lock();
const db = OpenDB();
const resultVideos = Sqlite.Exec(db, "SELECT COUNT(*) FROM videos;");
const resultQueue = Sqlite.Exec(db, "SELECT COUNT(*) FROM queue;");
Sqlite.Close(db);
const videoCount = TypeIsList(resultVideos) ? (ParseLong(resultVideos[0].["COUNT(*)"])??0) : 0;
const queueCount = TypeIsList(resultQueue) ? (ParseLong(resultQueue[0].["COUNT(*)"])??0) : 0;
const progress = {CurrentVideoProgress,CurrentVideo,QueueCount=queueCount, VideoCount=videoCount};
this.Mutex.Unlock();
return progress;
}
/^ /^
Get Video Queue count Get Video Queue count
^/ ^/
@@ -1211,18 +1230,25 @@ class TYTD.Downloader {
//Console.WriteLine(res); //Console.WriteLine(res);
if(TypeOf(res) != "Null") if(TypeOf(res) != "Null")
{ {
if(TypeIsDefined(res.TYTD = this)){ if(TypeIsDefined(res.TYTD = this)){
res.Progress = (progress)=>{ res.Progress = (progress)=>{
this.Mutex.Lock();
this.CurrentVideoProgress = progress; this.CurrentVideoProgress = progress;
this.Mutex.Unlock();
this.VideoProgress.Invoke(this, { this.VideoProgress.Invoke(this, {
Video = res.Video, Video = res.Video,
progress progress
}); });
}; };
this.Mutex.Lock();
this.CurrentVideo = res.Video; this.CurrentVideo = res.Video;
this.Mutex.Unlock();
this.VideoStarted.Invoke(this,{ this.VideoStarted.Invoke(this,{
Video = res.Video Video = res.Video

View File

@@ -1,4 +1,7 @@
# Changelog # Changelog
## 1.0.1
Target crosslang v0.0.10, add /api/v1/progress_ex.json
## 1.0.0 ## 1.0.0
Make it a little faster, queue is in sqlite now, fix db bug and more Make it a little faster, queue is in sqlite now, fix db bug and more

View File

@@ -9,7 +9,7 @@
name: "tytd2025" name: "tytd2025"
arch: "any" arch: "any"
platform: "linux" platform: "linux"
version: "1.0.0" version: "1.0.1"
section: "default" section: "default"
priority: "extra" priority: "extra"