From 744517cf88b97501fb3571f9be3eb7f28858c1fd Mon Sep 17 00:00:00 2001 From: WardPearce Date: Fri, 6 Mar 2026 23:12:29 +1300 Subject: [PATCH 01/16] Bump version --- materialious/android/app/build.gradle | 4 ++-- materialious/electron/materialious.metainfo.xml | 4 ++++ materialious/electron/package.json | 4 ++-- materialious/package.json | 4 ++-- materialious/static/localApi/ghContributors.json | 2 +- update_versions.py | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/materialious/android/app/build.gradle b/materialious/android/app/build.gradle index 7c42562c..a867ecd3 100644 --- a/materialious/android/app/build.gradle +++ b/materialious/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "us.materialio.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 228 - versionName "1.16.11" + versionCode 229 + versionName "1.16.12" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/materialious/electron/materialious.metainfo.xml b/materialious/electron/materialious.metainfo.xml index e77f4016..89d245d8 100644 --- a/materialious/electron/materialious.metainfo.xml +++ b/materialious/electron/materialious.metainfo.xml @@ -70,6 +70,10 @@ intense + + + https://github.com/Materialious/Materialious/releases/tag/1.16.12 + https://github.com/Materialious/Materialious/releases/tag/1.16.11 diff --git a/materialious/electron/package.json b/materialious/electron/package.json index 906d9f4c..e23383a7 100644 --- a/materialious/electron/package.json +++ b/materialious/electron/package.json @@ -1,6 +1,6 @@ { "name": "Materialious", - "version": "1.16.11", + "version": "1.16.12", "description": "Modern material design for YouTube and Invidious.", "author": { "name": "Ward Pearce", @@ -44,4 +44,4 @@ "capacitor", "electron" ] -} +} \ No newline at end of file diff --git a/materialious/package.json b/materialious/package.json index c0e2e50f..398049a7 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -1,6 +1,6 @@ { "name": "materialious", - "version": "1.16.11", + "version": "1.16.12", "private": true, "scripts": { "dev": "npm run patch:github && vite dev", @@ -99,4 +99,4 @@ "youtubei.js": "^16.0.1", "zod": "^4.3.6" } -} +} \ No newline at end of file diff --git a/materialious/static/localApi/ghContributors.json b/materialious/static/localApi/ghContributors.json index 8ee75184..32d6898b 100644 --- a/materialious/static/localApi/ghContributors.json +++ b/materialious/static/localApi/ghContributors.json @@ -1 +1 @@ -[{"login":"WardPearce","id":27844174,"node_id":"MDQ6VXNlcjI3ODQ0MTc0","avatar_url":"https://avatars.githubusercontent.com/u/27844174?v=4","gravatar_id":"","url":"https://api.github.com/users/WardPearce","html_url":"https://github.com/WardPearce","followers_url":"https://api.github.com/users/WardPearce/followers","following_url":"https://api.github.com/users/WardPearce/following{/other_user}","gists_url":"https://api.github.com/users/WardPearce/gists{/gist_id}","starred_url":"https://api.github.com/users/WardPearce/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WardPearce/subscriptions","organizations_url":"https://api.github.com/users/WardPearce/orgs","repos_url":"https://api.github.com/users/WardPearce/repos","events_url":"https://api.github.com/users/WardPearce/events{/privacy}","received_events_url":"https://api.github.com/users/WardPearce/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2286},{"login":"dependabot[bot]","id":49699333,"node_id":"MDM6Qm90NDk2OTkzMzM=","avatar_url":"https://avatars.githubusercontent.com/in/29110?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot%5Bbot%5D","html_url":"https://github.com/apps/dependabot","followers_url":"https://api.github.com/users/dependabot%5Bbot%5D/followers","following_url":"https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/dependabot%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/dependabot%5Bbot%5D/repos","events_url":"https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false,"contributions":241},{"login":"Lezurex","id":48829956,"node_id":"MDQ6VXNlcjQ4ODI5OTU2","avatar_url":"https://avatars.githubusercontent.com/u/48829956?v=4","gravatar_id":"","url":"https://api.github.com/users/Lezurex","html_url":"https://github.com/Lezurex","followers_url":"https://api.github.com/users/Lezurex/followers","following_url":"https://api.github.com/users/Lezurex/following{/other_user}","gists_url":"https://api.github.com/users/Lezurex/gists{/gist_id}","starred_url":"https://api.github.com/users/Lezurex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Lezurex/subscriptions","organizations_url":"https://api.github.com/users/Lezurex/orgs","repos_url":"https://api.github.com/users/Lezurex/repos","events_url":"https://api.github.com/users/Lezurex/events{/privacy}","received_events_url":"https://api.github.com/users/Lezurex/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":44},{"login":"ghostofsparta222","id":251893418,"node_id":"U_kgDODwOWqg","avatar_url":"https://avatars.githubusercontent.com/u/251893418?v=4","gravatar_id":"","url":"https://api.github.com/users/ghostofsparta222","html_url":"https://github.com/ghostofsparta222","followers_url":"https://api.github.com/users/ghostofsparta222/followers","following_url":"https://api.github.com/users/ghostofsparta222/following{/other_user}","gists_url":"https://api.github.com/users/ghostofsparta222/gists{/gist_id}","starred_url":"https://api.github.com/users/ghostofsparta222/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ghostofsparta222/subscriptions","organizations_url":"https://api.github.com/users/ghostofsparta222/orgs","repos_url":"https://api.github.com/users/ghostofsparta222/repos","events_url":"https://api.github.com/users/ghostofsparta222/events{/privacy}","received_events_url":"https://api.github.com/users/ghostofsparta222/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":15},{"login":"Coool","id":8421903,"node_id":"MDQ6VXNlcjg0MjE5MDM=","avatar_url":"https://avatars.githubusercontent.com/u/8421903?v=4","gravatar_id":"","url":"https://api.github.com/users/Coool","html_url":"https://github.com/Coool","followers_url":"https://api.github.com/users/Coool/followers","following_url":"https://api.github.com/users/Coool/following{/other_user}","gists_url":"https://api.github.com/users/Coool/gists{/gist_id}","starred_url":"https://api.github.com/users/Coool/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Coool/subscriptions","organizations_url":"https://api.github.com/users/Coool/orgs","repos_url":"https://api.github.com/users/Coool/repos","events_url":"https://api.github.com/users/Coool/events{/privacy}","received_events_url":"https://api.github.com/users/Coool/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":15},{"login":"araujosemacento","id":60983392,"node_id":"MDQ6VXNlcjYwOTgzMzky","avatar_url":"https://avatars.githubusercontent.com/u/60983392?v=4","gravatar_id":"","url":"https://api.github.com/users/araujosemacento","html_url":"https://github.com/araujosemacento","followers_url":"https://api.github.com/users/araujosemacento/followers","following_url":"https://api.github.com/users/araujosemacento/following{/other_user}","gists_url":"https://api.github.com/users/araujosemacento/gists{/gist_id}","starred_url":"https://api.github.com/users/araujosemacento/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/araujosemacento/subscriptions","organizations_url":"https://api.github.com/users/araujosemacento/orgs","repos_url":"https://api.github.com/users/araujosemacento/repos","events_url":"https://api.github.com/users/araujosemacento/events{/privacy}","received_events_url":"https://api.github.com/users/araujosemacento/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":14},{"login":"toolatebot","id":169580663,"node_id":"U_kgDOChuYdw","avatar_url":"https://avatars.githubusercontent.com/u/169580663?v=4","gravatar_id":"","url":"https://api.github.com/users/toolatebot","html_url":"https://github.com/toolatebot","followers_url":"https://api.github.com/users/toolatebot/followers","following_url":"https://api.github.com/users/toolatebot/following{/other_user}","gists_url":"https://api.github.com/users/toolatebot/gists{/gist_id}","starred_url":"https://api.github.com/users/toolatebot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/toolatebot/subscriptions","organizations_url":"https://api.github.com/users/toolatebot/orgs","repos_url":"https://api.github.com/users/toolatebot/repos","events_url":"https://api.github.com/users/toolatebot/events{/privacy}","received_events_url":"https://api.github.com/users/toolatebot/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":9},{"login":"Precific","id":30053082,"node_id":"MDQ6VXNlcjMwMDUzMDgy","avatar_url":"https://avatars.githubusercontent.com/u/30053082?v=4","gravatar_id":"","url":"https://api.github.com/users/Precific","html_url":"https://github.com/Precific","followers_url":"https://api.github.com/users/Precific/followers","following_url":"https://api.github.com/users/Precific/following{/other_user}","gists_url":"https://api.github.com/users/Precific/gists{/gist_id}","starred_url":"https://api.github.com/users/Precific/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Precific/subscriptions","organizations_url":"https://api.github.com/users/Precific/orgs","repos_url":"https://api.github.com/users/Precific/repos","events_url":"https://api.github.com/users/Precific/events{/privacy}","received_events_url":"https://api.github.com/users/Precific/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":9},{"login":"yzqzss","id":30341059,"node_id":"MDQ6VXNlcjMwMzQxMDU5","avatar_url":"https://avatars.githubusercontent.com/u/30341059?v=4","gravatar_id":"","url":"https://api.github.com/users/yzqzss","html_url":"https://github.com/yzqzss","followers_url":"https://api.github.com/users/yzqzss/followers","following_url":"https://api.github.com/users/yzqzss/following{/other_user}","gists_url":"https://api.github.com/users/yzqzss/gists{/gist_id}","starred_url":"https://api.github.com/users/yzqzss/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yzqzss/subscriptions","organizations_url":"https://api.github.com/users/yzqzss/orgs","repos_url":"https://api.github.com/users/yzqzss/repos","events_url":"https://api.github.com/users/yzqzss/events{/privacy}","received_events_url":"https://api.github.com/users/yzqzss/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":4},{"login":"thejenja","id":65224669,"node_id":"MDQ6VXNlcjY1MjI0NjY5","avatar_url":"https://avatars.githubusercontent.com/u/65224669?v=4","gravatar_id":"","url":"https://api.github.com/users/thejenja","html_url":"https://github.com/thejenja","followers_url":"https://api.github.com/users/thejenja/followers","following_url":"https://api.github.com/users/thejenja/following{/other_user}","gists_url":"https://api.github.com/users/thejenja/gists{/gist_id}","starred_url":"https://api.github.com/users/thejenja/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thejenja/subscriptions","organizations_url":"https://api.github.com/users/thejenja/orgs","repos_url":"https://api.github.com/users/thejenja/repos","events_url":"https://api.github.com/users/thejenja/events{/privacy}","received_events_url":"https://api.github.com/users/thejenja/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":4},{"login":"cookiennn","id":114363954,"node_id":"U_kgDOBtEOMg","avatar_url":"https://avatars.githubusercontent.com/u/114363954?v=4","gravatar_id":"","url":"https://api.github.com/users/cookiennn","html_url":"https://github.com/cookiennn","followers_url":"https://api.github.com/users/cookiennn/followers","following_url":"https://api.github.com/users/cookiennn/following{/other_user}","gists_url":"https://api.github.com/users/cookiennn/gists{/gist_id}","starred_url":"https://api.github.com/users/cookiennn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cookiennn/subscriptions","organizations_url":"https://api.github.com/users/cookiennn/orgs","repos_url":"https://api.github.com/users/cookiennn/repos","events_url":"https://api.github.com/users/cookiennn/events{/privacy}","received_events_url":"https://api.github.com/users/cookiennn/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":4},{"login":"SecularSteve","id":33793273,"node_id":"MDQ6VXNlcjMzNzkzMjcz","avatar_url":"https://avatars.githubusercontent.com/u/33793273?v=4","gravatar_id":"","url":"https://api.github.com/users/SecularSteve","html_url":"https://github.com/SecularSteve","followers_url":"https://api.github.com/users/SecularSteve/followers","following_url":"https://api.github.com/users/SecularSteve/following{/other_user}","gists_url":"https://api.github.com/users/SecularSteve/gists{/gist_id}","starred_url":"https://api.github.com/users/SecularSteve/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SecularSteve/subscriptions","organizations_url":"https://api.github.com/users/SecularSteve/orgs","repos_url":"https://api.github.com/users/SecularSteve/repos","events_url":"https://api.github.com/users/SecularSteve/events{/privacy}","received_events_url":"https://api.github.com/users/SecularSteve/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":4},{"login":"R0GGER","id":8298741,"node_id":"MDQ6VXNlcjgyOTg3NDE=","avatar_url":"https://avatars.githubusercontent.com/u/8298741?v=4","gravatar_id":"","url":"https://api.github.com/users/R0GGER","html_url":"https://github.com/R0GGER","followers_url":"https://api.github.com/users/R0GGER/followers","following_url":"https://api.github.com/users/R0GGER/following{/other_user}","gists_url":"https://api.github.com/users/R0GGER/gists{/gist_id}","starred_url":"https://api.github.com/users/R0GGER/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/R0GGER/subscriptions","organizations_url":"https://api.github.com/users/R0GGER/orgs","repos_url":"https://api.github.com/users/R0GGER/repos","events_url":"https://api.github.com/users/R0GGER/events{/privacy}","received_events_url":"https://api.github.com/users/R0GGER/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":3},{"login":"arcoast","id":81871508,"node_id":"MDQ6VXNlcjgxODcxNTA4","avatar_url":"https://avatars.githubusercontent.com/u/81871508?v=4","gravatar_id":"","url":"https://api.github.com/users/arcoast","html_url":"https://github.com/arcoast","followers_url":"https://api.github.com/users/arcoast/followers","following_url":"https://api.github.com/users/arcoast/following{/other_user}","gists_url":"https://api.github.com/users/arcoast/gists{/gist_id}","starred_url":"https://api.github.com/users/arcoast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arcoast/subscriptions","organizations_url":"https://api.github.com/users/arcoast/orgs","repos_url":"https://api.github.com/users/arcoast/repos","events_url":"https://api.github.com/users/arcoast/events{/privacy}","received_events_url":"https://api.github.com/users/arcoast/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":3},{"login":"coolvitto","id":160416811,"node_id":"U_kgDOCY_EKw","avatar_url":"https://avatars.githubusercontent.com/u/160416811?v=4","gravatar_id":"","url":"https://api.github.com/users/coolvitto","html_url":"https://github.com/coolvitto","followers_url":"https://api.github.com/users/coolvitto/followers","following_url":"https://api.github.com/users/coolvitto/following{/other_user}","gists_url":"https://api.github.com/users/coolvitto/gists{/gist_id}","starred_url":"https://api.github.com/users/coolvitto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coolvitto/subscriptions","organizations_url":"https://api.github.com/users/coolvitto/orgs","repos_url":"https://api.github.com/users/coolvitto/repos","events_url":"https://api.github.com/users/coolvitto/events{/privacy}","received_events_url":"https://api.github.com/users/coolvitto/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":3},{"login":"maboroshin","id":41102508,"node_id":"MDQ6VXNlcjQxMTAyNTA4","avatar_url":"https://avatars.githubusercontent.com/u/41102508?v=4","gravatar_id":"","url":"https://api.github.com/users/maboroshin","html_url":"https://github.com/maboroshin","followers_url":"https://api.github.com/users/maboroshin/followers","following_url":"https://api.github.com/users/maboroshin/following{/other_user}","gists_url":"https://api.github.com/users/maboroshin/gists{/gist_id}","starred_url":"https://api.github.com/users/maboroshin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/maboroshin/subscriptions","organizations_url":"https://api.github.com/users/maboroshin/orgs","repos_url":"https://api.github.com/users/maboroshin/repos","events_url":"https://api.github.com/users/maboroshin/events{/privacy}","received_events_url":"https://api.github.com/users/maboroshin/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":3},{"login":"pinembour","id":15938873,"node_id":"MDQ6VXNlcjE1OTM4ODcz","avatar_url":"https://avatars.githubusercontent.com/u/15938873?v=4","gravatar_id":"","url":"https://api.github.com/users/pinembour","html_url":"https://github.com/pinembour","followers_url":"https://api.github.com/users/pinembour/followers","following_url":"https://api.github.com/users/pinembour/following{/other_user}","gists_url":"https://api.github.com/users/pinembour/gists{/gist_id}","starred_url":"https://api.github.com/users/pinembour/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pinembour/subscriptions","organizations_url":"https://api.github.com/users/pinembour/orgs","repos_url":"https://api.github.com/users/pinembour/repos","events_url":"https://api.github.com/users/pinembour/events{/privacy}","received_events_url":"https://api.github.com/users/pinembour/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"lamarios","id":1192563,"node_id":"MDQ6VXNlcjExOTI1NjM=","avatar_url":"https://avatars.githubusercontent.com/u/1192563?v=4","gravatar_id":"","url":"https://api.github.com/users/lamarios","html_url":"https://github.com/lamarios","followers_url":"https://api.github.com/users/lamarios/followers","following_url":"https://api.github.com/users/lamarios/following{/other_user}","gists_url":"https://api.github.com/users/lamarios/gists{/gist_id}","starred_url":"https://api.github.com/users/lamarios/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lamarios/subscriptions","organizations_url":"https://api.github.com/users/lamarios/orgs","repos_url":"https://api.github.com/users/lamarios/repos","events_url":"https://api.github.com/users/lamarios/events{/privacy}","received_events_url":"https://api.github.com/users/lamarios/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"bonswouar","id":615053,"node_id":"MDQ6VXNlcjYxNTA1Mw==","avatar_url":"https://avatars.githubusercontent.com/u/615053?v=4","gravatar_id":"","url":"https://api.github.com/users/bonswouar","html_url":"https://github.com/bonswouar","followers_url":"https://api.github.com/users/bonswouar/followers","following_url":"https://api.github.com/users/bonswouar/following{/other_user}","gists_url":"https://api.github.com/users/bonswouar/gists{/gist_id}","starred_url":"https://api.github.com/users/bonswouar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bonswouar/subscriptions","organizations_url":"https://api.github.com/users/bonswouar/orgs","repos_url":"https://api.github.com/users/bonswouar/repos","events_url":"https://api.github.com/users/bonswouar/events{/privacy}","received_events_url":"https://api.github.com/users/bonswouar/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"Makhuta","id":23267923,"node_id":"MDQ6VXNlcjIzMjY3OTIz","avatar_url":"https://avatars.githubusercontent.com/u/23267923?v=4","gravatar_id":"","url":"https://api.github.com/users/Makhuta","html_url":"https://github.com/Makhuta","followers_url":"https://api.github.com/users/Makhuta/followers","following_url":"https://api.github.com/users/Makhuta/following{/other_user}","gists_url":"https://api.github.com/users/Makhuta/gists{/gist_id}","starred_url":"https://api.github.com/users/Makhuta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Makhuta/subscriptions","organizations_url":"https://api.github.com/users/Makhuta/orgs","repos_url":"https://api.github.com/users/Makhuta/repos","events_url":"https://api.github.com/users/Makhuta/events{/privacy}","received_events_url":"https://api.github.com/users/Makhuta/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"Midou36O","id":45198486,"node_id":"MDQ6VXNlcjQ1MTk4NDg2","avatar_url":"https://avatars.githubusercontent.com/u/45198486?v=4","gravatar_id":"","url":"https://api.github.com/users/Midou36O","html_url":"https://github.com/Midou36O","followers_url":"https://api.github.com/users/Midou36O/followers","following_url":"https://api.github.com/users/Midou36O/following{/other_user}","gists_url":"https://api.github.com/users/Midou36O/gists{/gist_id}","starred_url":"https://api.github.com/users/Midou36O/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Midou36O/subscriptions","organizations_url":"https://api.github.com/users/Midou36O/orgs","repos_url":"https://api.github.com/users/Midou36O/repos","events_url":"https://api.github.com/users/Midou36O/events{/privacy}","received_events_url":"https://api.github.com/users/Midou36O/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"Vladik01-11","id":70571714,"node_id":"MDQ6VXNlcjcwNTcxNzE0","avatar_url":"https://avatars.githubusercontent.com/u/70571714?v=4","gravatar_id":"","url":"https://api.github.com/users/Vladik01-11","html_url":"https://github.com/Vladik01-11","followers_url":"https://api.github.com/users/Vladik01-11/followers","following_url":"https://api.github.com/users/Vladik01-11/following{/other_user}","gists_url":"https://api.github.com/users/Vladik01-11/gists{/gist_id}","starred_url":"https://api.github.com/users/Vladik01-11/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Vladik01-11/subscriptions","organizations_url":"https://api.github.com/users/Vladik01-11/orgs","repos_url":"https://api.github.com/users/Vladik01-11/repos","events_url":"https://api.github.com/users/Vladik01-11/events{/privacy}","received_events_url":"https://api.github.com/users/Vladik01-11/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"yurtpage","id":155876573,"node_id":"U_kgDOCUp83Q","avatar_url":"https://avatars.githubusercontent.com/u/155876573?v=4","gravatar_id":"","url":"https://api.github.com/users/yurtpage","html_url":"https://github.com/yurtpage","followers_url":"https://api.github.com/users/yurtpage/followers","following_url":"https://api.github.com/users/yurtpage/following{/other_user}","gists_url":"https://api.github.com/users/yurtpage/gists{/gist_id}","starred_url":"https://api.github.com/users/yurtpage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yurtpage/subscriptions","organizations_url":"https://api.github.com/users/yurtpage/orgs","repos_url":"https://api.github.com/users/yurtpage/repos","events_url":"https://api.github.com/users/yurtpage/events{/privacy}","received_events_url":"https://api.github.com/users/yurtpage/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"HappyHippo77","id":30226559,"node_id":"MDQ6VXNlcjMwMjI2NTU5","avatar_url":"https://avatars.githubusercontent.com/u/30226559?v=4","gravatar_id":"","url":"https://api.github.com/users/HappyHippo77","html_url":"https://github.com/HappyHippo77","followers_url":"https://api.github.com/users/HappyHippo77/followers","following_url":"https://api.github.com/users/HappyHippo77/following{/other_user}","gists_url":"https://api.github.com/users/HappyHippo77/gists{/gist_id}","starred_url":"https://api.github.com/users/HappyHippo77/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HappyHippo77/subscriptions","organizations_url":"https://api.github.com/users/HappyHippo77/orgs","repos_url":"https://api.github.com/users/HappyHippo77/repos","events_url":"https://api.github.com/users/HappyHippo77/events{/privacy}","received_events_url":"https://api.github.com/users/HappyHippo77/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"IzzySoft","id":6781438,"node_id":"MDQ6VXNlcjY3ODE0Mzg=","avatar_url":"https://avatars.githubusercontent.com/u/6781438?v=4","gravatar_id":"","url":"https://api.github.com/users/IzzySoft","html_url":"https://github.com/IzzySoft","followers_url":"https://api.github.com/users/IzzySoft/followers","following_url":"https://api.github.com/users/IzzySoft/following{/other_user}","gists_url":"https://api.github.com/users/IzzySoft/gists{/gist_id}","starred_url":"https://api.github.com/users/IzzySoft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/IzzySoft/subscriptions","organizations_url":"https://api.github.com/users/IzzySoft/orgs","repos_url":"https://api.github.com/users/IzzySoft/repos","events_url":"https://api.github.com/users/IzzySoft/events{/privacy}","received_events_url":"https://api.github.com/users/IzzySoft/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"JamsRepos","id":1347620,"node_id":"MDQ6VXNlcjEzNDc2MjA=","avatar_url":"https://avatars.githubusercontent.com/u/1347620?v=4","gravatar_id":"","url":"https://api.github.com/users/JamsRepos","html_url":"https://github.com/JamsRepos","followers_url":"https://api.github.com/users/JamsRepos/followers","following_url":"https://api.github.com/users/JamsRepos/following{/other_user}","gists_url":"https://api.github.com/users/JamsRepos/gists{/gist_id}","starred_url":"https://api.github.com/users/JamsRepos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JamsRepos/subscriptions","organizations_url":"https://api.github.com/users/JamsRepos/orgs","repos_url":"https://api.github.com/users/JamsRepos/repos","events_url":"https://api.github.com/users/JamsRepos/events{/privacy}","received_events_url":"https://api.github.com/users/JamsRepos/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"koelle25","id":915514,"node_id":"MDQ6VXNlcjkxNTUxNA==","avatar_url":"https://avatars.githubusercontent.com/u/915514?v=4","gravatar_id":"","url":"https://api.github.com/users/koelle25","html_url":"https://github.com/koelle25","followers_url":"https://api.github.com/users/koelle25/followers","following_url":"https://api.github.com/users/koelle25/following{/other_user}","gists_url":"https://api.github.com/users/koelle25/gists{/gist_id}","starred_url":"https://api.github.com/users/koelle25/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/koelle25/subscriptions","organizations_url":"https://api.github.com/users/koelle25/orgs","repos_url":"https://api.github.com/users/koelle25/repos","events_url":"https://api.github.com/users/koelle25/events{/privacy}","received_events_url":"https://api.github.com/users/koelle25/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"patrickkempff","id":310766,"node_id":"MDQ6VXNlcjMxMDc2Ng==","avatar_url":"https://avatars.githubusercontent.com/u/310766?v=4","gravatar_id":"","url":"https://api.github.com/users/patrickkempff","html_url":"https://github.com/patrickkempff","followers_url":"https://api.github.com/users/patrickkempff/followers","following_url":"https://api.github.com/users/patrickkempff/following{/other_user}","gists_url":"https://api.github.com/users/patrickkempff/gists{/gist_id}","starred_url":"https://api.github.com/users/patrickkempff/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patrickkempff/subscriptions","organizations_url":"https://api.github.com/users/patrickkempff/orgs","repos_url":"https://api.github.com/users/patrickkempff/repos","events_url":"https://api.github.com/users/patrickkempff/events{/privacy}","received_events_url":"https://api.github.com/users/patrickkempff/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"Spaenny","id":5008508,"node_id":"MDQ6VXNlcjUwMDg1MDg=","avatar_url":"https://avatars.githubusercontent.com/u/5008508?v=4","gravatar_id":"","url":"https://api.github.com/users/Spaenny","html_url":"https://github.com/Spaenny","followers_url":"https://api.github.com/users/Spaenny/followers","following_url":"https://api.github.com/users/Spaenny/following{/other_user}","gists_url":"https://api.github.com/users/Spaenny/gists{/gist_id}","starred_url":"https://api.github.com/users/Spaenny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Spaenny/subscriptions","organizations_url":"https://api.github.com/users/Spaenny/orgs","repos_url":"https://api.github.com/users/Spaenny/repos","events_url":"https://api.github.com/users/Spaenny/events{/privacy}","received_events_url":"https://api.github.com/users/Spaenny/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"Poussinou","id":19646549,"node_id":"MDQ6VXNlcjE5NjQ2NTQ5","avatar_url":"https://avatars.githubusercontent.com/u/19646549?v=4","gravatar_id":"","url":"https://api.github.com/users/Poussinou","html_url":"https://github.com/Poussinou","followers_url":"https://api.github.com/users/Poussinou/followers","following_url":"https://api.github.com/users/Poussinou/following{/other_user}","gists_url":"https://api.github.com/users/Poussinou/gists{/gist_id}","starred_url":"https://api.github.com/users/Poussinou/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Poussinou/subscriptions","organizations_url":"https://api.github.com/users/Poussinou/orgs","repos_url":"https://api.github.com/users/Poussinou/repos","events_url":"https://api.github.com/users/Poussinou/events{/privacy}","received_events_url":"https://api.github.com/users/Poussinou/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1}] \ No newline at end of file +[{"login":"WardPearce","id":27844174,"node_id":"MDQ6VXNlcjI3ODQ0MTc0","avatar_url":"https://avatars.githubusercontent.com/u/27844174?v=4","gravatar_id":"","url":"https://api.github.com/users/WardPearce","html_url":"https://github.com/WardPearce","followers_url":"https://api.github.com/users/WardPearce/followers","following_url":"https://api.github.com/users/WardPearce/following{/other_user}","gists_url":"https://api.github.com/users/WardPearce/gists{/gist_id}","starred_url":"https://api.github.com/users/WardPearce/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WardPearce/subscriptions","organizations_url":"https://api.github.com/users/WardPearce/orgs","repos_url":"https://api.github.com/users/WardPearce/repos","events_url":"https://api.github.com/users/WardPearce/events{/privacy}","received_events_url":"https://api.github.com/users/WardPearce/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2295},{"login":"dependabot[bot]","id":49699333,"node_id":"MDM6Qm90NDk2OTkzMzM=","avatar_url":"https://avatars.githubusercontent.com/in/29110?v=4","gravatar_id":"","url":"https://api.github.com/users/dependabot%5Bbot%5D","html_url":"https://github.com/apps/dependabot","followers_url":"https://api.github.com/users/dependabot%5Bbot%5D/followers","following_url":"https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dependabot%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/dependabot%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/dependabot%5Bbot%5D/repos","events_url":"https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/dependabot%5Bbot%5D/received_events","type":"Bot","user_view_type":"public","site_admin":false,"contributions":241},{"login":"Lezurex","id":48829956,"node_id":"MDQ6VXNlcjQ4ODI5OTU2","avatar_url":"https://avatars.githubusercontent.com/u/48829956?v=4","gravatar_id":"","url":"https://api.github.com/users/Lezurex","html_url":"https://github.com/Lezurex","followers_url":"https://api.github.com/users/Lezurex/followers","following_url":"https://api.github.com/users/Lezurex/following{/other_user}","gists_url":"https://api.github.com/users/Lezurex/gists{/gist_id}","starred_url":"https://api.github.com/users/Lezurex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Lezurex/subscriptions","organizations_url":"https://api.github.com/users/Lezurex/orgs","repos_url":"https://api.github.com/users/Lezurex/repos","events_url":"https://api.github.com/users/Lezurex/events{/privacy}","received_events_url":"https://api.github.com/users/Lezurex/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":44},{"login":"ghostofsparta222","id":251893418,"node_id":"U_kgDODwOWqg","avatar_url":"https://avatars.githubusercontent.com/u/251893418?v=4","gravatar_id":"","url":"https://api.github.com/users/ghostofsparta222","html_url":"https://github.com/ghostofsparta222","followers_url":"https://api.github.com/users/ghostofsparta222/followers","following_url":"https://api.github.com/users/ghostofsparta222/following{/other_user}","gists_url":"https://api.github.com/users/ghostofsparta222/gists{/gist_id}","starred_url":"https://api.github.com/users/ghostofsparta222/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ghostofsparta222/subscriptions","organizations_url":"https://api.github.com/users/ghostofsparta222/orgs","repos_url":"https://api.github.com/users/ghostofsparta222/repos","events_url":"https://api.github.com/users/ghostofsparta222/events{/privacy}","received_events_url":"https://api.github.com/users/ghostofsparta222/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":15},{"login":"Coool","id":8421903,"node_id":"MDQ6VXNlcjg0MjE5MDM=","avatar_url":"https://avatars.githubusercontent.com/u/8421903?v=4","gravatar_id":"","url":"https://api.github.com/users/Coool","html_url":"https://github.com/Coool","followers_url":"https://api.github.com/users/Coool/followers","following_url":"https://api.github.com/users/Coool/following{/other_user}","gists_url":"https://api.github.com/users/Coool/gists{/gist_id}","starred_url":"https://api.github.com/users/Coool/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Coool/subscriptions","organizations_url":"https://api.github.com/users/Coool/orgs","repos_url":"https://api.github.com/users/Coool/repos","events_url":"https://api.github.com/users/Coool/events{/privacy}","received_events_url":"https://api.github.com/users/Coool/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":15},{"login":"araujosemacento","id":60983392,"node_id":"MDQ6VXNlcjYwOTgzMzky","avatar_url":"https://avatars.githubusercontent.com/u/60983392?v=4","gravatar_id":"","url":"https://api.github.com/users/araujosemacento","html_url":"https://github.com/araujosemacento","followers_url":"https://api.github.com/users/araujosemacento/followers","following_url":"https://api.github.com/users/araujosemacento/following{/other_user}","gists_url":"https://api.github.com/users/araujosemacento/gists{/gist_id}","starred_url":"https://api.github.com/users/araujosemacento/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/araujosemacento/subscriptions","organizations_url":"https://api.github.com/users/araujosemacento/orgs","repos_url":"https://api.github.com/users/araujosemacento/repos","events_url":"https://api.github.com/users/araujosemacento/events{/privacy}","received_events_url":"https://api.github.com/users/araujosemacento/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":14},{"login":"toolatebot","id":169580663,"node_id":"U_kgDOChuYdw","avatar_url":"https://avatars.githubusercontent.com/u/169580663?v=4","gravatar_id":"","url":"https://api.github.com/users/toolatebot","html_url":"https://github.com/toolatebot","followers_url":"https://api.github.com/users/toolatebot/followers","following_url":"https://api.github.com/users/toolatebot/following{/other_user}","gists_url":"https://api.github.com/users/toolatebot/gists{/gist_id}","starred_url":"https://api.github.com/users/toolatebot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/toolatebot/subscriptions","organizations_url":"https://api.github.com/users/toolatebot/orgs","repos_url":"https://api.github.com/users/toolatebot/repos","events_url":"https://api.github.com/users/toolatebot/events{/privacy}","received_events_url":"https://api.github.com/users/toolatebot/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":9},{"login":"Precific","id":30053082,"node_id":"MDQ6VXNlcjMwMDUzMDgy","avatar_url":"https://avatars.githubusercontent.com/u/30053082?v=4","gravatar_id":"","url":"https://api.github.com/users/Precific","html_url":"https://github.com/Precific","followers_url":"https://api.github.com/users/Precific/followers","following_url":"https://api.github.com/users/Precific/following{/other_user}","gists_url":"https://api.github.com/users/Precific/gists{/gist_id}","starred_url":"https://api.github.com/users/Precific/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Precific/subscriptions","organizations_url":"https://api.github.com/users/Precific/orgs","repos_url":"https://api.github.com/users/Precific/repos","events_url":"https://api.github.com/users/Precific/events{/privacy}","received_events_url":"https://api.github.com/users/Precific/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":9},{"login":"yzqzss","id":30341059,"node_id":"MDQ6VXNlcjMwMzQxMDU5","avatar_url":"https://avatars.githubusercontent.com/u/30341059?v=4","gravatar_id":"","url":"https://api.github.com/users/yzqzss","html_url":"https://github.com/yzqzss","followers_url":"https://api.github.com/users/yzqzss/followers","following_url":"https://api.github.com/users/yzqzss/following{/other_user}","gists_url":"https://api.github.com/users/yzqzss/gists{/gist_id}","starred_url":"https://api.github.com/users/yzqzss/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yzqzss/subscriptions","organizations_url":"https://api.github.com/users/yzqzss/orgs","repos_url":"https://api.github.com/users/yzqzss/repos","events_url":"https://api.github.com/users/yzqzss/events{/privacy}","received_events_url":"https://api.github.com/users/yzqzss/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":4},{"login":"thejenja","id":65224669,"node_id":"MDQ6VXNlcjY1MjI0NjY5","avatar_url":"https://avatars.githubusercontent.com/u/65224669?v=4","gravatar_id":"","url":"https://api.github.com/users/thejenja","html_url":"https://github.com/thejenja","followers_url":"https://api.github.com/users/thejenja/followers","following_url":"https://api.github.com/users/thejenja/following{/other_user}","gists_url":"https://api.github.com/users/thejenja/gists{/gist_id}","starred_url":"https://api.github.com/users/thejenja/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thejenja/subscriptions","organizations_url":"https://api.github.com/users/thejenja/orgs","repos_url":"https://api.github.com/users/thejenja/repos","events_url":"https://api.github.com/users/thejenja/events{/privacy}","received_events_url":"https://api.github.com/users/thejenja/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":4},{"login":"cookiennn","id":114363954,"node_id":"U_kgDOBtEOMg","avatar_url":"https://avatars.githubusercontent.com/u/114363954?v=4","gravatar_id":"","url":"https://api.github.com/users/cookiennn","html_url":"https://github.com/cookiennn","followers_url":"https://api.github.com/users/cookiennn/followers","following_url":"https://api.github.com/users/cookiennn/following{/other_user}","gists_url":"https://api.github.com/users/cookiennn/gists{/gist_id}","starred_url":"https://api.github.com/users/cookiennn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cookiennn/subscriptions","organizations_url":"https://api.github.com/users/cookiennn/orgs","repos_url":"https://api.github.com/users/cookiennn/repos","events_url":"https://api.github.com/users/cookiennn/events{/privacy}","received_events_url":"https://api.github.com/users/cookiennn/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":4},{"login":"SecularSteve","id":33793273,"node_id":"MDQ6VXNlcjMzNzkzMjcz","avatar_url":"https://avatars.githubusercontent.com/u/33793273?v=4","gravatar_id":"","url":"https://api.github.com/users/SecularSteve","html_url":"https://github.com/SecularSteve","followers_url":"https://api.github.com/users/SecularSteve/followers","following_url":"https://api.github.com/users/SecularSteve/following{/other_user}","gists_url":"https://api.github.com/users/SecularSteve/gists{/gist_id}","starred_url":"https://api.github.com/users/SecularSteve/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SecularSteve/subscriptions","organizations_url":"https://api.github.com/users/SecularSteve/orgs","repos_url":"https://api.github.com/users/SecularSteve/repos","events_url":"https://api.github.com/users/SecularSteve/events{/privacy}","received_events_url":"https://api.github.com/users/SecularSteve/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":4},{"login":"R0GGER","id":8298741,"node_id":"MDQ6VXNlcjgyOTg3NDE=","avatar_url":"https://avatars.githubusercontent.com/u/8298741?v=4","gravatar_id":"","url":"https://api.github.com/users/R0GGER","html_url":"https://github.com/R0GGER","followers_url":"https://api.github.com/users/R0GGER/followers","following_url":"https://api.github.com/users/R0GGER/following{/other_user}","gists_url":"https://api.github.com/users/R0GGER/gists{/gist_id}","starred_url":"https://api.github.com/users/R0GGER/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/R0GGER/subscriptions","organizations_url":"https://api.github.com/users/R0GGER/orgs","repos_url":"https://api.github.com/users/R0GGER/repos","events_url":"https://api.github.com/users/R0GGER/events{/privacy}","received_events_url":"https://api.github.com/users/R0GGER/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":3},{"login":"arcoast","id":81871508,"node_id":"MDQ6VXNlcjgxODcxNTA4","avatar_url":"https://avatars.githubusercontent.com/u/81871508?v=4","gravatar_id":"","url":"https://api.github.com/users/arcoast","html_url":"https://github.com/arcoast","followers_url":"https://api.github.com/users/arcoast/followers","following_url":"https://api.github.com/users/arcoast/following{/other_user}","gists_url":"https://api.github.com/users/arcoast/gists{/gist_id}","starred_url":"https://api.github.com/users/arcoast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arcoast/subscriptions","organizations_url":"https://api.github.com/users/arcoast/orgs","repos_url":"https://api.github.com/users/arcoast/repos","events_url":"https://api.github.com/users/arcoast/events{/privacy}","received_events_url":"https://api.github.com/users/arcoast/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":3},{"login":"coolvitto","id":160416811,"node_id":"U_kgDOCY_EKw","avatar_url":"https://avatars.githubusercontent.com/u/160416811?v=4","gravatar_id":"","url":"https://api.github.com/users/coolvitto","html_url":"https://github.com/coolvitto","followers_url":"https://api.github.com/users/coolvitto/followers","following_url":"https://api.github.com/users/coolvitto/following{/other_user}","gists_url":"https://api.github.com/users/coolvitto/gists{/gist_id}","starred_url":"https://api.github.com/users/coolvitto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coolvitto/subscriptions","organizations_url":"https://api.github.com/users/coolvitto/orgs","repos_url":"https://api.github.com/users/coolvitto/repos","events_url":"https://api.github.com/users/coolvitto/events{/privacy}","received_events_url":"https://api.github.com/users/coolvitto/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":3},{"login":"maboroshin","id":41102508,"node_id":"MDQ6VXNlcjQxMTAyNTA4","avatar_url":"https://avatars.githubusercontent.com/u/41102508?v=4","gravatar_id":"","url":"https://api.github.com/users/maboroshin","html_url":"https://github.com/maboroshin","followers_url":"https://api.github.com/users/maboroshin/followers","following_url":"https://api.github.com/users/maboroshin/following{/other_user}","gists_url":"https://api.github.com/users/maboroshin/gists{/gist_id}","starred_url":"https://api.github.com/users/maboroshin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/maboroshin/subscriptions","organizations_url":"https://api.github.com/users/maboroshin/orgs","repos_url":"https://api.github.com/users/maboroshin/repos","events_url":"https://api.github.com/users/maboroshin/events{/privacy}","received_events_url":"https://api.github.com/users/maboroshin/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":3},{"login":"pinembour","id":15938873,"node_id":"MDQ6VXNlcjE1OTM4ODcz","avatar_url":"https://avatars.githubusercontent.com/u/15938873?v=4","gravatar_id":"","url":"https://api.github.com/users/pinembour","html_url":"https://github.com/pinembour","followers_url":"https://api.github.com/users/pinembour/followers","following_url":"https://api.github.com/users/pinembour/following{/other_user}","gists_url":"https://api.github.com/users/pinembour/gists{/gist_id}","starred_url":"https://api.github.com/users/pinembour/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pinembour/subscriptions","organizations_url":"https://api.github.com/users/pinembour/orgs","repos_url":"https://api.github.com/users/pinembour/repos","events_url":"https://api.github.com/users/pinembour/events{/privacy}","received_events_url":"https://api.github.com/users/pinembour/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"lamarios","id":1192563,"node_id":"MDQ6VXNlcjExOTI1NjM=","avatar_url":"https://avatars.githubusercontent.com/u/1192563?v=4","gravatar_id":"","url":"https://api.github.com/users/lamarios","html_url":"https://github.com/lamarios","followers_url":"https://api.github.com/users/lamarios/followers","following_url":"https://api.github.com/users/lamarios/following{/other_user}","gists_url":"https://api.github.com/users/lamarios/gists{/gist_id}","starred_url":"https://api.github.com/users/lamarios/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lamarios/subscriptions","organizations_url":"https://api.github.com/users/lamarios/orgs","repos_url":"https://api.github.com/users/lamarios/repos","events_url":"https://api.github.com/users/lamarios/events{/privacy}","received_events_url":"https://api.github.com/users/lamarios/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"bonswouar","id":615053,"node_id":"MDQ6VXNlcjYxNTA1Mw==","avatar_url":"https://avatars.githubusercontent.com/u/615053?v=4","gravatar_id":"","url":"https://api.github.com/users/bonswouar","html_url":"https://github.com/bonswouar","followers_url":"https://api.github.com/users/bonswouar/followers","following_url":"https://api.github.com/users/bonswouar/following{/other_user}","gists_url":"https://api.github.com/users/bonswouar/gists{/gist_id}","starred_url":"https://api.github.com/users/bonswouar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bonswouar/subscriptions","organizations_url":"https://api.github.com/users/bonswouar/orgs","repos_url":"https://api.github.com/users/bonswouar/repos","events_url":"https://api.github.com/users/bonswouar/events{/privacy}","received_events_url":"https://api.github.com/users/bonswouar/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"Makhuta","id":23267923,"node_id":"MDQ6VXNlcjIzMjY3OTIz","avatar_url":"https://avatars.githubusercontent.com/u/23267923?v=4","gravatar_id":"","url":"https://api.github.com/users/Makhuta","html_url":"https://github.com/Makhuta","followers_url":"https://api.github.com/users/Makhuta/followers","following_url":"https://api.github.com/users/Makhuta/following{/other_user}","gists_url":"https://api.github.com/users/Makhuta/gists{/gist_id}","starred_url":"https://api.github.com/users/Makhuta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Makhuta/subscriptions","organizations_url":"https://api.github.com/users/Makhuta/orgs","repos_url":"https://api.github.com/users/Makhuta/repos","events_url":"https://api.github.com/users/Makhuta/events{/privacy}","received_events_url":"https://api.github.com/users/Makhuta/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"Midou36O","id":45198486,"node_id":"MDQ6VXNlcjQ1MTk4NDg2","avatar_url":"https://avatars.githubusercontent.com/u/45198486?v=4","gravatar_id":"","url":"https://api.github.com/users/Midou36O","html_url":"https://github.com/Midou36O","followers_url":"https://api.github.com/users/Midou36O/followers","following_url":"https://api.github.com/users/Midou36O/following{/other_user}","gists_url":"https://api.github.com/users/Midou36O/gists{/gist_id}","starred_url":"https://api.github.com/users/Midou36O/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Midou36O/subscriptions","organizations_url":"https://api.github.com/users/Midou36O/orgs","repos_url":"https://api.github.com/users/Midou36O/repos","events_url":"https://api.github.com/users/Midou36O/events{/privacy}","received_events_url":"https://api.github.com/users/Midou36O/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"Vladik01-11","id":70571714,"node_id":"MDQ6VXNlcjcwNTcxNzE0","avatar_url":"https://avatars.githubusercontent.com/u/70571714?v=4","gravatar_id":"","url":"https://api.github.com/users/Vladik01-11","html_url":"https://github.com/Vladik01-11","followers_url":"https://api.github.com/users/Vladik01-11/followers","following_url":"https://api.github.com/users/Vladik01-11/following{/other_user}","gists_url":"https://api.github.com/users/Vladik01-11/gists{/gist_id}","starred_url":"https://api.github.com/users/Vladik01-11/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Vladik01-11/subscriptions","organizations_url":"https://api.github.com/users/Vladik01-11/orgs","repos_url":"https://api.github.com/users/Vladik01-11/repos","events_url":"https://api.github.com/users/Vladik01-11/events{/privacy}","received_events_url":"https://api.github.com/users/Vladik01-11/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"yurtpage","id":155876573,"node_id":"U_kgDOCUp83Q","avatar_url":"https://avatars.githubusercontent.com/u/155876573?v=4","gravatar_id":"","url":"https://api.github.com/users/yurtpage","html_url":"https://github.com/yurtpage","followers_url":"https://api.github.com/users/yurtpage/followers","following_url":"https://api.github.com/users/yurtpage/following{/other_user}","gists_url":"https://api.github.com/users/yurtpage/gists{/gist_id}","starred_url":"https://api.github.com/users/yurtpage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yurtpage/subscriptions","organizations_url":"https://api.github.com/users/yurtpage/orgs","repos_url":"https://api.github.com/users/yurtpage/repos","events_url":"https://api.github.com/users/yurtpage/events{/privacy}","received_events_url":"https://api.github.com/users/yurtpage/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":2},{"login":"HappyHippo77","id":30226559,"node_id":"MDQ6VXNlcjMwMjI2NTU5","avatar_url":"https://avatars.githubusercontent.com/u/30226559?v=4","gravatar_id":"","url":"https://api.github.com/users/HappyHippo77","html_url":"https://github.com/HappyHippo77","followers_url":"https://api.github.com/users/HappyHippo77/followers","following_url":"https://api.github.com/users/HappyHippo77/following{/other_user}","gists_url":"https://api.github.com/users/HappyHippo77/gists{/gist_id}","starred_url":"https://api.github.com/users/HappyHippo77/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HappyHippo77/subscriptions","organizations_url":"https://api.github.com/users/HappyHippo77/orgs","repos_url":"https://api.github.com/users/HappyHippo77/repos","events_url":"https://api.github.com/users/HappyHippo77/events{/privacy}","received_events_url":"https://api.github.com/users/HappyHippo77/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"IzzySoft","id":6781438,"node_id":"MDQ6VXNlcjY3ODE0Mzg=","avatar_url":"https://avatars.githubusercontent.com/u/6781438?v=4","gravatar_id":"","url":"https://api.github.com/users/IzzySoft","html_url":"https://github.com/IzzySoft","followers_url":"https://api.github.com/users/IzzySoft/followers","following_url":"https://api.github.com/users/IzzySoft/following{/other_user}","gists_url":"https://api.github.com/users/IzzySoft/gists{/gist_id}","starred_url":"https://api.github.com/users/IzzySoft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/IzzySoft/subscriptions","organizations_url":"https://api.github.com/users/IzzySoft/orgs","repos_url":"https://api.github.com/users/IzzySoft/repos","events_url":"https://api.github.com/users/IzzySoft/events{/privacy}","received_events_url":"https://api.github.com/users/IzzySoft/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"JamsRepos","id":1347620,"node_id":"MDQ6VXNlcjEzNDc2MjA=","avatar_url":"https://avatars.githubusercontent.com/u/1347620?v=4","gravatar_id":"","url":"https://api.github.com/users/JamsRepos","html_url":"https://github.com/JamsRepos","followers_url":"https://api.github.com/users/JamsRepos/followers","following_url":"https://api.github.com/users/JamsRepos/following{/other_user}","gists_url":"https://api.github.com/users/JamsRepos/gists{/gist_id}","starred_url":"https://api.github.com/users/JamsRepos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JamsRepos/subscriptions","organizations_url":"https://api.github.com/users/JamsRepos/orgs","repos_url":"https://api.github.com/users/JamsRepos/repos","events_url":"https://api.github.com/users/JamsRepos/events{/privacy}","received_events_url":"https://api.github.com/users/JamsRepos/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"koelle25","id":915514,"node_id":"MDQ6VXNlcjkxNTUxNA==","avatar_url":"https://avatars.githubusercontent.com/u/915514?v=4","gravatar_id":"","url":"https://api.github.com/users/koelle25","html_url":"https://github.com/koelle25","followers_url":"https://api.github.com/users/koelle25/followers","following_url":"https://api.github.com/users/koelle25/following{/other_user}","gists_url":"https://api.github.com/users/koelle25/gists{/gist_id}","starred_url":"https://api.github.com/users/koelle25/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/koelle25/subscriptions","organizations_url":"https://api.github.com/users/koelle25/orgs","repos_url":"https://api.github.com/users/koelle25/repos","events_url":"https://api.github.com/users/koelle25/events{/privacy}","received_events_url":"https://api.github.com/users/koelle25/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"patrickkempff","id":310766,"node_id":"MDQ6VXNlcjMxMDc2Ng==","avatar_url":"https://avatars.githubusercontent.com/u/310766?v=4","gravatar_id":"","url":"https://api.github.com/users/patrickkempff","html_url":"https://github.com/patrickkempff","followers_url":"https://api.github.com/users/patrickkempff/followers","following_url":"https://api.github.com/users/patrickkempff/following{/other_user}","gists_url":"https://api.github.com/users/patrickkempff/gists{/gist_id}","starred_url":"https://api.github.com/users/patrickkempff/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patrickkempff/subscriptions","organizations_url":"https://api.github.com/users/patrickkempff/orgs","repos_url":"https://api.github.com/users/patrickkempff/repos","events_url":"https://api.github.com/users/patrickkempff/events{/privacy}","received_events_url":"https://api.github.com/users/patrickkempff/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"Spaenny","id":5008508,"node_id":"MDQ6VXNlcjUwMDg1MDg=","avatar_url":"https://avatars.githubusercontent.com/u/5008508?v=4","gravatar_id":"","url":"https://api.github.com/users/Spaenny","html_url":"https://github.com/Spaenny","followers_url":"https://api.github.com/users/Spaenny/followers","following_url":"https://api.github.com/users/Spaenny/following{/other_user}","gists_url":"https://api.github.com/users/Spaenny/gists{/gist_id}","starred_url":"https://api.github.com/users/Spaenny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Spaenny/subscriptions","organizations_url":"https://api.github.com/users/Spaenny/orgs","repos_url":"https://api.github.com/users/Spaenny/repos","events_url":"https://api.github.com/users/Spaenny/events{/privacy}","received_events_url":"https://api.github.com/users/Spaenny/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1},{"login":"Poussinou","id":19646549,"node_id":"MDQ6VXNlcjE5NjQ2NTQ5","avatar_url":"https://avatars.githubusercontent.com/u/19646549?v=4","gravatar_id":"","url":"https://api.github.com/users/Poussinou","html_url":"https://github.com/Poussinou","followers_url":"https://api.github.com/users/Poussinou/followers","following_url":"https://api.github.com/users/Poussinou/following{/other_user}","gists_url":"https://api.github.com/users/Poussinou/gists{/gist_id}","starred_url":"https://api.github.com/users/Poussinou/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Poussinou/subscriptions","organizations_url":"https://api.github.com/users/Poussinou/orgs","repos_url":"https://api.github.com/users/Poussinou/repos","events_url":"https://api.github.com/users/Poussinou/events{/privacy}","received_events_url":"https://api.github.com/users/Poussinou/received_events","type":"User","user_view_type":"public","site_admin":false,"contributions":1}] \ No newline at end of file diff --git a/update_versions.py b/update_versions.py index 55fe5b10..e9bf3666 100644 --- a/update_versions.py +++ b/update_versions.py @@ -3,7 +3,7 @@ import os import re from datetime import datetime -LATEST_VERSION = "1.16.11" +LATEST_VERSION = "1.16.12" RELEASE_DATE = datetime.now().strftime("%Y-%-m-%d") # Format: YYYY-M-D WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious") From 73e1e609c8375679eb2b43bceb516dff29bbe629 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Fri, 6 Mar 2026 23:12:39 +1300 Subject: [PATCH 02/16] Started invidious import --- materialious/src/lib/api/index.ts | 10 +- .../src/lib/components/settings/Engine.svelte | 31 ++--- .../components/settings/ExportImport.svelte | 21 ++++ .../lib/components/settings/Settings.svelte | 10 +- materialious/src/lib/i18n/locales/en.json | 5 + materialious/src/lib/importExport.ts | 108 ++++++++++++++++++ 6 files changed, 156 insertions(+), 29 deletions(-) create mode 100644 materialious/src/lib/components/settings/ExportImport.svelte create mode 100644 materialious/src/lib/importExport.ts diff --git a/materialious/src/lib/api/index.ts b/materialious/src/lib/api/index.ts index ea9c5dc1..e2d3854e 100644 --- a/materialious/src/lib/api/index.ts +++ b/materialious/src/lib/api/index.ts @@ -257,15 +257,15 @@ export async function amSubscribed( export async function postSubscribe( authorId: string, - fetchOptions: RequestInit = {}, - bypassYTBackend: boolean = false + authorName: string | undefined = undefined, + fetchOptions: RequestInit = {} ) { - if (isYTBackend() && !bypassYTBackend) { + if (isYTBackend()) { if (isOwnBackend()?.internalAuth && get(rawMasterKeyStore)) { - return postSubscribeBackend(authorId); + return postSubscribeBackend(authorId, authorName); } - return postSubscribeYTjs(authorId); + return postSubscribeYTjs(authorId, authorName); } return postSubscribeInvidious(authorId, fetchOptions); diff --git a/materialious/src/lib/components/settings/Engine.svelte b/materialious/src/lib/components/settings/Engine.svelte index 2c81fa92..56cb9370 100644 --- a/materialious/src/lib/components/settings/Engine.svelte +++ b/materialious/src/lib/components/settings/Engine.svelte @@ -7,16 +7,14 @@ engineFallbacksStore, engineMaxConcurrentChannelsStore, invidiousInstanceStore, - backendInUseStore, - rawMasterKeyStore + backendInUseStore } from '$lib/store'; import { useEngineFallback, type EngineFallback } from '$lib/api/misc'; import { get } from 'svelte/store'; - import { getSubscriptions, postSubscribe } from '$lib/api'; - import { postSubscribeYTjs } from '$lib/api/youtubejs/subscriptions'; + import { getSubscriptions } from '$lib/api'; import { addToast } from '../Toast.svelte'; - import { isOwnBackend } from '$lib/shared'; - import { postSubscribeBackend } from '$lib/api/backend/subscriptions'; + import { importSubscriptions } from '$lib/importExport'; + import { postSubscribeInvidious } from '$lib/api/invidious/subscribe'; const engineFallbacks: EngineFallback[] = [ 'Channel', @@ -56,20 +54,7 @@ } }); - let subscribeFunction: (id: string, name: string) => Promise; - - if (isOwnBackend()?.internalAuth && $rawMasterKeyStore) { - subscribeFunction = postSubscribeBackend; - } else { - subscribeFunction = postSubscribeYTjs; - } - - const subPromises: Promise[] = []; - importedSubs.forEach((sub) => { - subPromises.push(subscribeFunction(sub.authorId, sub.author)); - }); - - await Promise.all(subPromises); + await importSubscriptions(importedSubs); addToast({ data: { @@ -88,9 +73,9 @@ }); const subPromises: Promise[] = []; - subs.forEach((sub) => { - subPromises.push(postSubscribe(sub.authorId, {}, true)); - }); + for (const sub of subs) { + subPromises.push(postSubscribeInvidious(sub.authorId)); + } await Promise.all(subPromises); diff --git a/materialious/src/lib/components/settings/ExportImport.svelte b/materialious/src/lib/components/settings/ExportImport.svelte new file mode 100644 index 00000000..cecd1121 --- /dev/null +++ b/materialious/src/lib/components/settings/ExportImport.svelte @@ -0,0 +1,21 @@ + + +
+ + { + const files = (event.target as HTMLInputElement).files; + if (files?.length === 0 || !files) return; + + await importSubscriptionsFromFile(files[0]); + }} + type="file" + /> +
+

info {$_('layout.export.importSubSupported')}

diff --git a/materialious/src/lib/components/settings/Settings.svelte b/materialious/src/lib/components/settings/Settings.svelte index 36388a3c..9609e3fd 100644 --- a/materialious/src/lib/components/settings/Settings.svelte +++ b/materialious/src/lib/components/settings/Settings.svelte @@ -15,6 +15,7 @@ import { Tabs } from 'melt/builders'; import { mergeAttrs } from 'melt'; import Filters from './Filters.svelte'; + import ExportImport from './ExportImport.svelte'; type TabCategories = | 'interface' @@ -25,7 +26,8 @@ | 'about' | 'engine' | 'account' - | 'filters'; + | 'filters' + | 'export'; const tabCategories: Tabs = new Tabs({ value: 'interface', @@ -50,6 +52,12 @@ icon: 'keyboard_double_arrow_down', component: DeArrow }, + { + id: 'export', + label: $_('layout.export.title'), + icon: 'file_export', + component: ExportImport + }, { id: 'about', label: $_('layout.about'), diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json index 47f75e40..31715ece 100644 --- a/materialious/src/lib/i18n/locales/en.json +++ b/materialious/src/lib/i18n/locales/en.json @@ -271,6 +271,11 @@ "addConditional": "Add conditional", "addFilter": "Add filter" }, + "export": { + "title": "Export/Import", + "importSub": "Import subscriptions", + "importSubSupported": "YouTube CSV/OPML, Invidious OPML/JSON, Freetube database & NewPipe JSON supported" + }, "deArrow": { "title": "DeArrow", "thumbnailInstanceUrl": "Thumbnail instance URL", diff --git a/materialious/src/lib/importExport.ts b/materialious/src/lib/importExport.ts new file mode 100644 index 00000000..07a5377a --- /dev/null +++ b/materialious/src/lib/importExport.ts @@ -0,0 +1,108 @@ +import z from 'zod'; +import type { Subscription } from './api/model'; +import { getChannel, postSubscribe } from './api'; + +const zInvidiousSubs = z.object({ + subscriptions: z.array(z.string()) +}); + +const zFreetubeSubs = z.object({ + subscriptions: z.array( + z.object({ + id: z.string(), + name: z.string(), + thumbnail: z.string() + }) + ) +}); + +export async function importSubscriptions(subscriptions: Subscription[]) { + const subPromises: Promise[] = []; + for (const sub of subscriptions) { + subPromises.push(postSubscribe(sub.authorId, sub.author)); + } + + await Promise.all(subPromises); +} + +export async function importSubscriptionsFromFile(file: File) { + const fileContents = await file.text(); + + const subsToImport: Subscription[] = []; + + if (fileContents.startsWith(' | undefined; + try { + fileJson = JSON.parse(fileContents); + } catch { + // Continue regardless + } + + if (fileJson) { + const invidiousSubs = zInvidiousSubs.safeParse(fileJson); + const freetubeSubs = zFreetubeSubs.safeParse(fileJson); + + if (invidiousSubs.success) { + for (const authorId of invidiousSubs.data.subscriptions) { + try { + const channel = await getChannel(authorId); + subsToImport.push({ + authorId, + author: channel.author + }); + } catch { + // Continue regardless + } + } + } else if (freetubeSubs.success) { + for (const sub of freetubeSubs.data.subscriptions) { + subsToImport.push({ + author: sub.name, + authorId: sub.id + }); + } + } + } + } + + if (subsToImport.length > 0) { + await importSubscriptions(subsToImport); + return; + } + + throw new Error('Unable to determine imported file type'); +} From 13b5d87c9da5f3284b4c41a404a6cf7d1f41c5d3 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Fri, 6 Mar 2026 23:38:58 +1300 Subject: [PATCH 03/16] Implemented importing subs via different file types --- materialious/package-lock.json | 22 +++++++++- materialious/package.json | 4 +- materialious/src/lib/importExport.ts | 62 +++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/materialious/package-lock.json b/materialious/package-lock.json index 9af80fd0..6520b6b0 100644 --- a/materialious/package-lock.json +++ b/materialious/package-lock.json @@ -1,12 +1,12 @@ { "name": "materialious", - "version": "1.16.8", + "version": "1.16.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "materialious", - "version": "1.16.8", + "version": "1.16.12", "hasInstallScript": true, "dependencies": { "@capacitor-community/electron": "^5.0.1", @@ -47,6 +47,7 @@ "melt": "^0.44.0", "mousetrap": "^1.6.5", "mysql2": "^3.18.2", + "papaparse": "^5.5.3", "pg": "^8.19.0", "pg-hstore": "^2.3.4", "safe-regex2": "^5.0.0", @@ -76,6 +77,7 @@ "@types/human-number": "^1.0.2", "@types/jsdom": "^28.0.0", "@types/mousetrap": "^1.6.15", + "@types/papaparse": "^5.5.2", "@vite-pwa/sveltekit": "^1.0.0", "eslint": "^10.0.2", "eslint-config-prettier": "^10.1.8", @@ -6125,6 +6127,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/papaparse": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.2.tgz", + "integrity": "sha512-gFnFp/JMzLHCwRf7tQHrNnfhN4eYBVYYI897CGX4MY1tzY9l2aLkVyx2IlKZ/SAqDbB3I1AOZW5gTMGGsqWliA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/phoenix": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", @@ -14525,6 +14537,12 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "license": "MIT" + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", diff --git a/materialious/package.json b/materialious/package.json index 398049a7..5f7f56e7 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -30,6 +30,7 @@ "@types/human-number": "^1.0.2", "@types/jsdom": "^28.0.0", "@types/mousetrap": "^1.6.15", + "@types/papaparse": "^5.5.2", "@vite-pwa/sveltekit": "^1.0.0", "eslint": "^10.0.2", "eslint-config-prettier": "^10.1.8", @@ -85,6 +86,7 @@ "melt": "^0.44.0", "mousetrap": "^1.6.5", "mysql2": "^3.18.2", + "papaparse": "^5.5.3", "pg": "^8.19.0", "pg-hstore": "^2.3.4", "safe-regex2": "^5.0.0", @@ -99,4 +101,4 @@ "youtubei.js": "^16.0.1", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/materialious/src/lib/importExport.ts b/materialious/src/lib/importExport.ts index 07a5377a..2f51979d 100644 --- a/materialious/src/lib/importExport.ts +++ b/materialious/src/lib/importExport.ts @@ -1,6 +1,7 @@ import z from 'zod'; import type { Subscription } from './api/model'; import { getChannel, postSubscribe } from './api'; +import Papa from 'papaparse'; const zInvidiousSubs = z.object({ subscriptions: z.array(z.string()) @@ -16,6 +17,32 @@ const zFreetubeSubs = z.object({ ) }); +const zNewPipeSubs = z.object({ + subscriptions: z.array( + z.object({ + service_id: z.number(), + url: z.url(), + name: z.string() + }) + ) +}); + +const zYouTubeSubs = z.array( + z.object({ + snippet: z.object({ + channelId: z.string(), + title: z.string() + }) + }) +); + +const zYouTubeCsvSubs = z.array( + z.object({ + 'Channel ID': z.string(), + 'Channel title': z.string() + }) +); + export async function importSubscriptions(subscriptions: Subscription[]) { const subPromises: Promise[] = []; for (const sub of subscriptions) { @@ -64,7 +91,7 @@ export async function importSubscriptionsFromFile(file: File) { authorId: channelId }); } - } else { + } else if (file.name.endsWith('.json')) { let fileJson: Record | undefined; try { fileJson = JSON.parse(fileContents); @@ -75,6 +102,8 @@ export async function importSubscriptionsFromFile(file: File) { if (fileJson) { const invidiousSubs = zInvidiousSubs.safeParse(fileJson); const freetubeSubs = zFreetubeSubs.safeParse(fileJson); + const newPipeSubs = zNewPipeSubs.safeParse(fileJson); + const youtubeSubs = zYouTubeSubs.safeParse(fileJson); if (invidiousSubs.success) { for (const authorId of invidiousSubs.data.subscriptions) { @@ -95,6 +124,37 @@ export async function importSubscriptionsFromFile(file: File) { authorId: sub.id }); } + } else if (newPipeSubs.success) { + for (const sub of newPipeSubs.data.subscriptions) { + const authorId = sub.url.split('/')[4]; + if (typeof authorId !== 'string') continue; + subsToImport.push({ + author: sub.name, + authorId + }); + } + } else if (youtubeSubs.success) { + for (const sub of youtubeSubs.data) { + subsToImport.push({ + author: sub.snippet.title, + authorId: sub.snippet.channelId + }); + } + } + } + } else if (file.name.endsWith('.csv')) { + const csv = Papa.parse(fileContents, { header: true, skipEmptyLines: true }); + + if (csv.data.length > 0) { + const youtubeSubs = zYouTubeCsvSubs.safeParse(csv.data); + + if (youtubeSubs.success) { + for (const sub of youtubeSubs.data) { + subsToImport.push({ + author: sub['Channel title'], + authorId: sub['Channel ID'] + }); + } } } } From f699d7c42235eb5aee526ddfa16411066fb9dfdf Mon Sep 17 00:00:00 2001 From: WardPearce Date: Fri, 6 Mar 2026 23:43:24 +1300 Subject: [PATCH 04/16] Added import toast --- materialious/src/lib/components/Toast.svelte | 2 +- .../lib/components/settings/ExportImport.svelte | 17 +++++++++++++++++ materialious/src/lib/i18n/locales/en.json | 4 +++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/materialious/src/lib/components/Toast.svelte b/materialious/src/lib/components/Toast.svelte index c1c984a6..6350c609 100644 --- a/materialious/src/lib/components/Toast.svelte +++ b/materialious/src/lib/components/Toast.svelte @@ -11,7 +11,7 @@ action?: ToastAction; } - const toaster = new Toaster(); + export const toaster = new Toaster(); export const addToast = toaster.addToast; diff --git a/materialious/src/lib/components/settings/ExportImport.svelte b/materialious/src/lib/components/settings/ExportImport.svelte index cecd1121..3d9ed330 100644 --- a/materialious/src/lib/components/settings/ExportImport.svelte +++ b/materialious/src/lib/components/settings/ExportImport.svelte @@ -1,6 +1,7 @@
@@ -13,7 +14,23 @@ const files = (event.target as HTMLInputElement).files; if (files?.length === 0 || !files) return; + addToast({ + data: { + text: $_('layout.export.importing') + }, + id: 'importing-toast', + closeDelay: 0 + }); + await importSubscriptionsFromFile(files[0]); + + toaster.removeToast('importing-toast'); + + addToast({ + data: { + text: $_('layout.export.importingCompleted') + } + }); }} type="file" /> diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json index 31715ece..c5589347 100644 --- a/materialious/src/lib/i18n/locales/en.json +++ b/materialious/src/lib/i18n/locales/en.json @@ -274,7 +274,9 @@ "export": { "title": "Export/Import", "importSub": "Import subscriptions", - "importSubSupported": "YouTube CSV/OPML, Invidious OPML/JSON, Freetube database & NewPipe JSON supported" + "importSubSupported": "YouTube CSV/OPML, Invidious OPML/JSON, Freetube database & NewPipe JSON supported", + "importing": "Importing subscriptions (this will take a moment)", + "importingCompleted": "Subscriptions successfully imported" }, "deArrow": { "title": "DeArrow", From 18bb744969c457c5e1a6cb2689f8a61506a8cf49 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Fri, 6 Mar 2026 23:52:00 +1300 Subject: [PATCH 05/16] Improved engine auto export/import --- materialious/src/lib/api/index.ts | 7 ++----- .../src/lib/components/settings/Engine.svelte | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/materialious/src/lib/api/index.ts b/materialious/src/lib/api/index.ts index e2d3854e..2d4565e0 100644 --- a/materialious/src/lib/api/index.ts +++ b/materialious/src/lib/api/index.ts @@ -220,11 +220,8 @@ export async function notificationsMarkAsRead(fetchOptions: RequestInit = {}) { return notificationsMarkAsReadInvidious(fetchOptions); } -export async function getSubscriptions( - fetchOptions: RequestInit = {}, - bypassYTBackend: boolean = false -): Promise { - if (isYTBackend() && !bypassYTBackend) { +export async function getSubscriptions(fetchOptions: RequestInit = {}): Promise { + if (isYTBackend()) { if (isOwnBackend()?.internalAuth && get(rawMasterKeyStore)) { return (await getSubscriptionsBackend()).map((sub) => { return { diff --git a/materialious/src/lib/components/settings/Engine.svelte b/materialious/src/lib/components/settings/Engine.svelte index 56cb9370..2455f89e 100644 --- a/materialious/src/lib/components/settings/Engine.svelte +++ b/materialious/src/lib/components/settings/Engine.svelte @@ -12,9 +12,10 @@ import { useEngineFallback, type EngineFallback } from '$lib/api/misc'; import { get } from 'svelte/store'; import { getSubscriptions } from '$lib/api'; - import { addToast } from '../Toast.svelte'; + import { addToast, toaster } from '../Toast.svelte'; import { importSubscriptions } from '$lib/importExport'; import { postSubscribeInvidious } from '$lib/api/invidious/subscribe'; + import { getSubscriptionsInvidious } from '$lib/api/invidious/feed'; const engineFallbacks: EngineFallback[] = [ 'Channel', @@ -46,16 +47,20 @@ } async function importInvidiousSubs() { - const importedSubs = await getSubscriptions({}, true); + const importedSubs = await getSubscriptionsInvidious(); addToast({ data: { text: $_('layout.backendEngine.importingToMaterialious') - } + }, + id: 'import-to-materialious', + closeDelay: 0 }); await importSubscriptions(importedSubs); + toaster.removeToast('import-to-materialious'); + addToast({ data: { text: $_('layout.backendEngine.importingToMaterialiousFinished') @@ -69,7 +74,9 @@ addToast({ data: { text: $_('layout.backendEngine.exportingToInvidious') - } + }, + id: 'export-to-invidious', + closeDelay: 0 }); const subPromises: Promise[] = []; @@ -79,6 +86,8 @@ await Promise.all(subPromises); + toaster.removeToast('export-to-invidious'); + addToast({ data: { text: $_('layout.backendEngine.exportingToInvidiousFinished') From 62ba5905ac54d2756be1197979b014dcd6a4b9b7 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sat, 7 Mar 2026 00:08:55 +1300 Subject: [PATCH 06/16] Added export support --- .../components/settings/ExportImport.svelte | 19 +++++- materialious/src/lib/i18n/locales/en.json | 3 +- materialious/src/lib/importExport.ts | 61 ++++++++++++++++++- materialious/src/lib/misc.ts | 14 +++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/materialious/src/lib/components/settings/ExportImport.svelte b/materialious/src/lib/components/settings/ExportImport.svelte index 3d9ed330..708d0e4e 100644 --- a/materialious/src/lib/components/settings/ExportImport.svelte +++ b/materialious/src/lib/components/settings/ExportImport.svelte @@ -1,6 +1,6 @@ @@ -36,3 +36,20 @@ />

info {$_('layout.export.importSubSupported')}

+ +
+ +
+ + +
  • exportSubscriptionsAsFile('InvidiousJSON')}> + Invidious JSON +
  • +
  • exportSubscriptionsAsFile('OPML')}> + FreeTube/NewPipe/YouTube OPML +
  • +
    +
    diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json index c5589347..2246f6e9 100644 --- a/materialious/src/lib/i18n/locales/en.json +++ b/materialious/src/lib/i18n/locales/en.json @@ -276,7 +276,8 @@ "importSub": "Import subscriptions", "importSubSupported": "YouTube CSV/OPML, Invidious OPML/JSON, Freetube database & NewPipe JSON supported", "importing": "Importing subscriptions (this will take a moment)", - "importingCompleted": "Subscriptions successfully imported" + "importingCompleted": "Subscriptions successfully imported", + "exportSub": "Export subscriptions" }, "deArrow": { "title": "DeArrow", diff --git a/materialious/src/lib/importExport.ts b/materialious/src/lib/importExport.ts index 2f51979d..a44d3241 100644 --- a/materialious/src/lib/importExport.ts +++ b/materialious/src/lib/importExport.ts @@ -1,7 +1,8 @@ import z from 'zod'; import type { Subscription } from './api/model'; -import { getChannel, postSubscribe } from './api'; +import { getChannel, getSubscriptions, postSubscribe } from './api'; import Papa from 'papaparse'; +import { downloadStringAsFile } from './misc'; const zInvidiousSubs = z.object({ subscriptions: z.array(z.string()) @@ -166,3 +167,61 @@ export async function importSubscriptionsFromFile(file: File) { throw new Error('Unable to determine imported file type'); } + +export async function exportSubscriptionsAsFile(exportType: 'InvidiousJSON' | 'OPML') { + const subscriptions = await getSubscriptions(); + + let data: string | undefined; + + if (exportType === 'InvidiousJSON') { + const subIds = []; + + for (const sub of subscriptions) { + subIds.push(sub.authorId); + } + + data = JSON.stringify({ + subscriptions: subIds + }); + } else { + const xmlDoc = document.implementation.createDocument('', '', null); + + const opml = xmlDoc.createElement('opml'); + opml.setAttribute('version', '1.1'); + + const body = xmlDoc.createElement('body'); + + const mainOutline = xmlDoc.createElement('outline'); + mainOutline.setAttribute('text', 'YouTube Subscriptions'); + mainOutline.setAttribute('title', 'YouTube Subscriptions'); + + body.appendChild(mainOutline); + + for (const sub of subscriptions) { + const outline = xmlDoc.createElement('outline'); + outline.setAttribute('text', sub.author); + outline.setAttribute('title', sub.author); + outline.setAttribute('type', 'rss'); + outline.setAttribute( + 'xmlUrl', + `https://www.youtube.com/feeds/videos.xml?channel_id=${sub.authorId}` + ); + + body.appendChild(outline); + } + + opml.appendChild(body); + xmlDoc.appendChild(opml); + + const serializer = new XMLSerializer(); + + data = serializer.serializeToString(xmlDoc); + } + + if (data) { + downloadStringAsFile( + data, + `materialious-export-${new Date().toDateString().replaceAll(' ', '')}.${exportType === 'InvidiousJSON' ? 'json' : 'opml'}` + ); + } +} diff --git a/materialious/src/lib/misc.ts b/materialious/src/lib/misc.ts index 1fec17d6..9988a359 100644 --- a/materialious/src/lib/misc.ts +++ b/materialious/src/lib/misc.ts @@ -118,3 +118,17 @@ export function isUnrestrictedPlatform(): boolean { export function isYTBackend(): boolean { return get(backendInUseStore) === 'yt' && isUnrestrictedPlatform(); } + +export function downloadStringAsFile(content: string, filename: string) { + const blob = new Blob([content]); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = filename; + + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); +} From 546443cb6292e71a4e862dfe4c1c0037227d47a5 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sat, 7 Mar 2026 00:13:18 +1300 Subject: [PATCH 07/16] Update en.json --- materialious/src/lib/i18n/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json index 2246f6e9..d0576b84 100644 --- a/materialious/src/lib/i18n/locales/en.json +++ b/materialious/src/lib/i18n/locales/en.json @@ -274,7 +274,7 @@ "export": { "title": "Export/Import", "importSub": "Import subscriptions", - "importSubSupported": "YouTube CSV/OPML, Invidious OPML/JSON, Freetube database & NewPipe JSON supported", + "importSubSupported": "YouTube CSV/OPML/JSON, Invidious OPML/JSON, Freetube database & NewPipe JSON supported", "importing": "Importing subscriptions (this will take a moment)", "importingCompleted": "Subscriptions successfully imported", "exportSub": "Export subscriptions" From 0cb9675cddc1d2ddceefc38e7377967c6a55ad0d Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sat, 7 Mar 2026 00:20:11 +1300 Subject: [PATCH 08/16] Move automatic youtube backend import/export to export/import tab --- .../src/lib/components/settings/Engine.svelte | 73 ------------------ .../components/settings/ExportImport.svelte | 76 ++++++++++++++++++- materialious/src/lib/i18n/locales/en.json | 2 +- 3 files changed, 75 insertions(+), 76 deletions(-) diff --git a/materialious/src/lib/components/settings/Engine.svelte b/materialious/src/lib/components/settings/Engine.svelte index 2455f89e..05cd7419 100644 --- a/materialious/src/lib/components/settings/Engine.svelte +++ b/materialious/src/lib/components/settings/Engine.svelte @@ -1,21 +1,14 @@
    @@ -138,23 +82,6 @@ /> - - {#if $invidiousAuthStore && $invidiousInstanceStore} -
    {$_('layout.backendEngine.importExport')}
    -
    - - - -
    - - - {/if} {:else}
    {$_('layout.backendEngine.fallbacks')}
    {#each engineFallbacks as fallback (fallback)} diff --git a/materialious/src/lib/components/settings/ExportImport.svelte b/materialious/src/lib/components/settings/ExportImport.svelte index 708d0e4e..c2d345de 100644 --- a/materialious/src/lib/components/settings/ExportImport.svelte +++ b/materialious/src/lib/components/settings/ExportImport.svelte @@ -2,10 +2,64 @@ import { _ } from '$lib/i18n'; import { exportSubscriptionsAsFile, importSubscriptionsFromFile } from '$lib/importExport'; import { addToast, toaster } from '../Toast.svelte'; + import { getSubscriptions } from '$lib/api'; + import { importSubscriptions } from '$lib/importExport'; + import { postSubscribeInvidious } from '$lib/api/invidious/subscribe'; + import { getSubscriptionsInvidious } from '$lib/api/invidious/feed'; + import { backendInUseStore, invidiousAuthStore, invidiousInstanceStore } from '$lib/store'; + + async function importInvidiousSubs() { + const importedSubs = await getSubscriptionsInvidious(); + + addToast({ + data: { + text: $_('layout.backendEngine.importingToMaterialious') + }, + id: 'import-to-materialious', + closeDelay: 0 + }); + + await importSubscriptions(importedSubs); + + toaster.removeToast('import-to-materialious'); + + addToast({ + data: { + text: $_('layout.backendEngine.importingToMaterialiousFinished') + } + }); + } + + async function exportMaterialiousSubs() { + const subs = await getSubscriptions(); + + addToast({ + data: { + text: $_('layout.backendEngine.exportingToInvidious') + }, + id: 'export-to-invidious', + closeDelay: 0 + }); + + const subPromises: Promise[] = []; + for (const sub of subs) { + subPromises.push(postSubscribeInvidious(sub.authorId)); + } + + await Promise.all(subPromises); + + toaster.removeToast('export-to-invidious'); + + addToast({ + data: { + text: $_('layout.backendEngine.exportingToInvidiousFinished') + } + }); + }
    - @@ -40,7 +94,7 @@
    - @@ -53,3 +107,21 @@
    + +{#if $invidiousAuthStore && $invidiousInstanceStore && $backendInUseStore === 'yt'} +
    +
    {$_('layout.backendEngine.importExport')}
    +
    + + + +
    + + +{/if} diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json index d0576b84..2d997c5a 100644 --- a/materialious/src/lib/i18n/locales/en.json +++ b/materialious/src/lib/i18n/locales/en.json @@ -210,7 +210,7 @@ "cooldown": "Re-fetch channel hour cooldown", "concurrent": "Max amount of channels to parse before feed return", "fallbacks": "Fallbacks", - "importExport": "Import/Export subscriptions", + "importExport": "Automatic export/import", "exportToInvidious": "Export to Invidious from Materialious", "importToMaterialious": "Import from Invidious to Materialious", "importingToMaterialious": "Importing to Materialious began", From 185a408b78a33a936da25cd92e9af4e172408058 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sat, 7 Mar 2026 00:22:26 +1300 Subject: [PATCH 09/16] Check if already subscribed before adding record --- materialious/src/lib/server/user.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/materialious/src/lib/server/user.ts b/materialious/src/lib/server/user.ts index fea9cd66..0cd7f2aa 100644 --- a/materialious/src/lib/server/user.ts +++ b/materialious/src/lib/server/user.ts @@ -51,6 +51,8 @@ export class User { } async addSubscription(subscription: Omit) { + if (await this.amSubscribed(subscription.id)) return; + await getSequelize().ChannelSubscriptionTable.create({ ...subscription, UserId: this.id From 2f41258db8a192ff617c2bfaa4dd303ff15d6cd8 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sat, 7 Mar 2026 00:40:10 +1300 Subject: [PATCH 10/16] Minor layout improvements --- materialious/src/lib/components/settings/Engine.svelte | 4 ++-- materialious/src/lib/components/settings/ExportImport.svelte | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/materialious/src/lib/components/settings/Engine.svelte b/materialious/src/lib/components/settings/Engine.svelte index 05cd7419..a5603d0b 100644 --- a/materialious/src/lib/components/settings/Engine.svelte +++ b/materialious/src/lib/components/settings/Engine.svelte @@ -45,7 +45,7 @@
    {#if $backendInUseStore === 'yt'} -
    Feed
    +

    Feed

    view_stream {$_('layout.backendEngine.concurrent')}
    {:else} -
    {$_('layout.backendEngine.fallbacks')}
    +

    {$_('layout.backendEngine.fallbacks')}

    {#each engineFallbacks as fallback (fallback)}