mirror of
https://github.com/VueTubeApp/VueTube
synced 2024-11-22 19:25:16 +00:00
Merge github.com-picklenik:Frontesque/VueTube into dev
This commit is contained in:
commit
9634b69af8
13 changed files with 93 additions and 333 deletions
141
.github/workflows/nightly-release.yml
vendored
141
.github/workflows/nightly-release.yml
vendored
|
@ -1,141 +0,0 @@
|
|||
name: nightly-release
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
# Triggers the workflow on on a schedule
|
||||
schedule:
|
||||
# Runs "at 0:00 UTC every day" (see https://crontab.guru)
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NODE_VERSION: 16
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
name: Build web assets
|
||||
runs-on: ubuntu-latest
|
||||
needs: check_date
|
||||
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: main
|
||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- name: Install dependencies
|
||||
run: npm i; cd NUXT; npm i
|
||||
- name: Set App Version
|
||||
working-directory: NUXT
|
||||
run: sed -i 's/dev-local/${{ github.sha }}/' nuxt.config.js
|
||||
- name: Build web assets
|
||||
working-directory: NUXT
|
||||
run: npm run generate
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
android:
|
||||
name: Build Android platform
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Copy web assets to native platform
|
||||
run: npx cap copy android
|
||||
- name: Update native platform
|
||||
run: npx cap update android
|
||||
- name: Build with Gradle
|
||||
working-directory: android
|
||||
run: chmod +x gradlew; ./gradlew clean assembleRelease -x test -Pandroid.injected.signing.store.file=/home/runner/work/VueTube/VueTube/android/key.jks -Pandroid.injected.signing.store.password=${{ secrets.ANDROID_STORE_PASSWORD }} -Pandroid.injected.signing.key.alias=${{ secrets.ANDROID_KEY_ALIAS }} -Pandroid.injected.signing.key.password=${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: android
|
||||
path: android/app/build/outputs/apk/release/app-release.apk
|
||||
|
||||
ios:
|
||||
name: Build iOS platform
|
||||
runs-on: macos-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
- name: Copy web assets to native platform
|
||||
run: npx cap copy ios
|
||||
- name: Update native platform
|
||||
run: npx cap update ios
|
||||
- name: Add empty `GoogleService-Info.plist`
|
||||
run: echo "$GOOGLE_SERVICE_INFO_PLIST" > ios/App/App/GoogleService-Info.plist
|
||||
env:
|
||||
GOOGLE_SERVICE_INFO_PLIST: ${{secrets.GOOGLE_SERVICE_INFO_PLIST}}
|
||||
- name: Build and archive with xcodebuild
|
||||
working-directory: ios
|
||||
run: xcodebuild
|
||||
-workspace App/App.xcworkspace
|
||||
-scheme App
|
||||
-archivePath App/build/App.xarchive
|
||||
clean build archive
|
||||
CODE_SIGN_IDENTITY=""
|
||||
CODE_SIGNING_REQUIRED=NO
|
||||
CODE_SIGNING_ALLOWED="NO"
|
||||
CODE_SIGN_ENTITLEMENTS=""
|
||||
- name: Make IPA
|
||||
run: mkdir Payload && mv ~/Library/Developer/Xcode/DerivedData/App-*/Build/Products/Debug-iphoneos/App.app Payload && zip -r Payload.zip Payload && mv Payload.zip VueTube.ipa
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: iOS
|
||||
path: VueTube.ipa
|
||||
|
||||
check_date:
|
||||
runs-on: ubuntu-latest
|
||||
name: Check latest commit
|
||||
outputs:
|
||||
should_run: ${{ steps.should_run.outputs.should_run }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: print latest_commit
|
||||
run: echo ${{ github.sha }}
|
||||
|
||||
- id: should_run
|
||||
continue-on-error: true
|
||||
name: check latest commit is less than a day
|
||||
if: ${{ github.event_name == 'schedule' }}
|
||||
run: test -z $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "::set-output name=should_run::false"
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
<div style="margin: 1em;">
|
||||
<v-btn style="width: 100%;" class="primary text-none" @click="pickFile()"><v-icon style="margin-right: 0.5em;">mdi-sd</v-icon> Install from storage</v-btn>
|
||||
<input type="file" id="filePicker" accept="js" />
|
||||
</div>
|
||||
|
||||
<center v-if="plugins.length == 0" style="margin-top: 2em;">
|
||||
<v-icon size="50px">mdi-connection</v-icon>
|
||||
<h2>No plugins installed</h2>
|
||||
</center>
|
||||
|
||||
<!-- sorry for the mess, I will make a dumb (styles only) standardized card component later - Nik -->
|
||||
<div
|
||||
v-for="(plugin, index) in plugins"
|
||||
|
@ -43,16 +54,46 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#filePicker {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
plugins: new Array(),
|
||||
plugins: [],
|
||||
installedVersion: process.env.appVersion,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
this.plugins = await this.$tppl.list;
|
||||
//this.plugins = await this.$tppl.list;
|
||||
|
||||
const vm = this;
|
||||
document.getElementById('filePicker').onchange = async function() {
|
||||
const file = document.getElementById("filePicker").files[0];
|
||||
const contents = await vm.readFileContent(file);
|
||||
await vm.$tppl.addPlugin(contents);
|
||||
};
|
||||
|
||||
|
||||
},
|
||||
methods: {
|
||||
readFileContent(file) {
|
||||
const reader = new FileReader()
|
||||
return new Promise((resolve, reject) => {
|
||||
reader.onload = event => resolve(event.target.result)
|
||||
reader.onerror = error => reject(error)
|
||||
reader.readAsText(file)
|
||||
})
|
||||
},
|
||||
|
||||
pickFile() {
|
||||
document.getElementById("filePicker").click();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -105,9 +105,7 @@ export default {
|
|||
{
|
||||
name: "Plugins",
|
||||
icon: "mdi-puzzle",
|
||||
to: "",
|
||||
to: "/mods/plugins",
|
||||
disabled: true,
|
||||
to: "/mods/plugins"
|
||||
},
|
||||
{
|
||||
name: "Updates",
|
||||
|
|
|
@ -18,8 +18,7 @@ const ytApiVal = {
|
|||
};
|
||||
|
||||
const filesystem = {
|
||||
plugins: "vuetube/plugins",
|
||||
temp: "vuetube/temp",
|
||||
plugins: "plugins"
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -13,7 +13,7 @@ module.exports = {
|
|||
* Execute code on ALL VueTube pages
|
||||
************************/
|
||||
global: function() {
|
||||
|
||||
console.log("This code is now executed when ALL vuetube pages (including settings) are loaded.")
|
||||
},
|
||||
|
||||
/*************************
|
||||
|
|
|
@ -3,16 +3,10 @@ import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
|
|||
import { fs } from './constants';
|
||||
|
||||
//--- Set Up App Directory ---//
|
||||
const APP_DIRECTORY = Directory.Documents;
|
||||
|
||||
const APP_DIRECTORY = Directory.Data;
|
||||
|
||||
//--- Ensure Plugins Folder ---//
|
||||
const ensureStructure = new Promise(async (resolve, reject) => {
|
||||
const perms = await Filesystem.checkPermissions();
|
||||
if (perms.publicStorage !== "granted") {
|
||||
perms = await Filesystem.requestPermissions();
|
||||
}
|
||||
|
||||
//--- Ensure Plugins Folder ---//
|
||||
try {
|
||||
await Filesystem.mkdir({
|
||||
directory: APP_DIRECTORY, recursive: true,
|
||||
|
@ -20,52 +14,37 @@ const ensureStructure = new Promise(async (resolve, reject) => {
|
|||
});
|
||||
} catch (e) { /* Exists */ }
|
||||
|
||||
//--- Ensure Temp Folder ---//
|
||||
try {
|
||||
await Filesystem.mkdir({
|
||||
directory: APP_DIRECTORY, recursive: true,
|
||||
path: fs.temp,
|
||||
});
|
||||
} catch (e) { /* Exists */ }
|
||||
|
||||
perms
|
||||
? resolve(true)
|
||||
: reject(false)
|
||||
|
||||
resolve();
|
||||
})
|
||||
|
||||
|
||||
const module = {
|
||||
|
||||
|
||||
//--- Get Plugins ---//
|
||||
//--- List Plugins ---//
|
||||
list: new Promise(async (resolve, reject) => {
|
||||
let plugins = new Array();
|
||||
|
||||
if (await !ensureStructure) reject("Invalid Structure");
|
||||
await ensureStructure();
|
||||
|
||||
// Temp Plugin List
|
||||
plugins = Filesystem.readdir({
|
||||
directory: APP_DIRECTORY,
|
||||
path: fs.plugins
|
||||
})
|
||||
// End Temp Plugin List
|
||||
const plugins = await Filesystem.readdir({
|
||||
path: fs.plugins,
|
||||
directory: APP_DIRECTORY
|
||||
}).catch(err => { reject(err) })
|
||||
resolve(plugins);
|
||||
|
||||
}),
|
||||
//--- End Get Plugins ---//
|
||||
|
||||
//--- Delete Plugin ---//
|
||||
list: async (pluginName) => {
|
||||
|
||||
console.log(fs.plugins);
|
||||
/*
|
||||
const contents = await Filesystem.readFile({
|
||||
path: 'secrets/text.txt',
|
||||
directory: Directory.Documents,
|
||||
encoding: Encoding.UTF8,
|
||||
});
|
||||
*/
|
||||
async addPlugin(content) {
|
||||
await ensureStructure();
|
||||
new Promise(async (resolve, reject) => {
|
||||
const fileName = require("./utils").getCpn(); // Im not sure what this is actually meant for but im using it as a random string generator
|
||||
console.log("Saving Plugin As"+ fileName)
|
||||
await Filesystem.writeFile({
|
||||
path: fs.plugins+"/"+fileName+".js",
|
||||
directory: APP_DIRECTORY,
|
||||
data: content,
|
||||
encoding: Encoding.UTF8,
|
||||
});
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -134,28 +134,22 @@ const module = {
|
|||
humanTime(seconds = 0) {
|
||||
seconds = Math.floor(seconds); // Not doing this seems to break the calculation
|
||||
let levels = [
|
||||
Math.floor(seconds / 31536000), //Years
|
||||
Math.floor((seconds % 31536000) / 86400), //Days
|
||||
Math.floor(((seconds % 31536000) % 86400) / 3600), //Hours
|
||||
Math.floor(seconds / 31536000) || null, //Years
|
||||
Math.floor((seconds % 31536000) / 86400) || null, //Days
|
||||
Math.floor(((seconds % 31536000) % 86400) / 3600) || null, //Hours
|
||||
Math.floor((((seconds % 31536000) % 86400) % 3600) / 60), //Minutes
|
||||
(((seconds % 31536000) % 86400) % 3600) % 60, //Seconds
|
||||
Math.floor((((seconds % 31536000) % 86400) % 3600) % 60), //Seconds
|
||||
];
|
||||
levels = levels.filter((level) => level !== null);
|
||||
|
||||
let returntext = new String();
|
||||
for (const i in levels) {
|
||||
const num =
|
||||
levels[i].toString().length == 1 ? "0" + levels[i] : levels[i]; // If Number Is Single Digit, Add 0 In Front
|
||||
returntext += ":" + num;
|
||||
for (let i = 1; i < levels.length; i++) {
|
||||
levels[i] = levels[i].toString().padStart(2, "0");
|
||||
}
|
||||
while (returntext.startsWith(":00")) {
|
||||
returntext = returntext.substring(3);
|
||||
} // Remove Prepending 0s (eg. 00:00:00:01:00)
|
||||
if (returntext.startsWith(":0")) {
|
||||
returntext = returntext.substring(2);
|
||||
} else {
|
||||
returntext = returntext.substring(1);
|
||||
} // Prevent Time Starting With 0 (eg. 01:00)
|
||||
// console.log("Human Time:", returntext);
|
||||
|
||||
// join the array into a string with : as a separator
|
||||
const returntext = levels.join(":");
|
||||
|
||||
console.log("Human Time:", returntext);
|
||||
return returntext;
|
||||
},
|
||||
//--- End Convert Time To Human Readable String ---//
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||
|
@ -75,7 +74,5 @@
|
|||
|
||||
<!-- Permissions -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -2,9 +2,5 @@
|
|||
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<access origin="*" />
|
||||
|
||||
<feature name="CDVOrientation">
|
||||
<param name="android-package" value="cordova.plugins.screenorientation.CDVOrientation"/>
|
||||
</feature>
|
||||
|
||||
|
||||
</widget>
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package cordova.plugins.screenorientation;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.util.Log;
|
||||
|
||||
public class CDVOrientation extends CordovaPlugin {
|
||||
|
||||
private static final String TAG = "YoikScreenOrientation";
|
||||
|
||||
/**
|
||||
* Screen Orientation Constants
|
||||
*/
|
||||
|
||||
private static final String ANY = "any";
|
||||
private static final String PORTRAIT_PRIMARY = "portrait-primary";
|
||||
private static final String PORTRAIT_SECONDARY = "portrait-secondary";
|
||||
private static final String LANDSCAPE_PRIMARY = "landscape-primary";
|
||||
private static final String LANDSCAPE_SECONDARY = "landscape-secondary";
|
||||
private static final String PORTRAIT = "portrait";
|
||||
private static final String LANDSCAPE = "landscape";
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
|
||||
Log.d(TAG, "execute action: " + action);
|
||||
|
||||
// Route the Action
|
||||
if (action.equals("screenOrientation")) {
|
||||
return routeScreenOrientation(args, callbackContext);
|
||||
}
|
||||
|
||||
// Action not found
|
||||
callbackContext.error("action not recognised");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean routeScreenOrientation(JSONArray args, CallbackContext callbackContext) {
|
||||
|
||||
String action = args.optString(0);
|
||||
|
||||
|
||||
|
||||
String orientation = args.optString(1);
|
||||
|
||||
Log.d(TAG, "Requested ScreenOrientation: " + orientation);
|
||||
|
||||
Activity activity = cordova.getActivity();
|
||||
|
||||
if (orientation.equals(ANY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
} else if (orientation.equals(LANDSCAPE_PRIMARY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
} else if (orientation.equals(PORTRAIT_PRIMARY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
} else if (orientation.equals(LANDSCAPE)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||
} else if (orientation.equals(PORTRAIT)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
|
||||
} else if (orientation.equals(LANDSCAPE_SECONDARY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
|
||||
} else if (orientation.equals(PORTRAIT_SECONDARY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -2,9 +2,5 @@
|
|||
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<access origin="*" />
|
||||
|
||||
<feature name="CDVOrientation">
|
||||
<param name="ios-package" value="CDVOrientation"/>
|
||||
</feature>
|
||||
|
||||
|
||||
</widget>
|
|
@ -9,18 +9,17 @@ install! 'cocoapods', :disable_input_output_paths => true
|
|||
def capacitor_pods
|
||||
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCommunityHttp', :path => '../../node_modules/@capacitor-community/http'
|
||||
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
|
||||
pod 'CapacitorBrowser', :path => '../../node_modules/@capacitor/browser'
|
||||
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
|
||||
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
|
||||
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
|
||||
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
|
||||
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
|
||||
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
||||
pod 'CapacitorToast', :path => '../../node_modules/@capacitor/toast'
|
||||
pod 'HugotomaziCapacitorNavigationBar', :path => '../../node_modules/@hugotomazi/capacitor-navigation-bar'
|
||||
pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
|
||||
pod 'CapacitorCommunityHttp', :path => '..\..\node_modules\@capacitor-community\http'
|
||||
pod 'CapacitorApp', :path => '..\..\node_modules\@capacitor\app'
|
||||
pod 'CapacitorBrowser', :path => '..\..\node_modules\@capacitor\browser'
|
||||
pod 'CapacitorDevice', :path => '..\..\node_modules\@capacitor\device'
|
||||
pod 'CapacitorFilesystem', :path => '..\..\node_modules\@capacitor\filesystem'
|
||||
pod 'CapacitorHaptics', :path => '..\..\node_modules\@capacitor\haptics'
|
||||
pod 'CapacitorShare', :path => '..\..\node_modules\@capacitor\share'
|
||||
pod 'CapacitorSplashScreen', :path => '..\..\node_modules\@capacitor\splash-screen'
|
||||
pod 'CapacitorStatusBar', :path => '..\..\node_modules\@capacitor\status-bar'
|
||||
pod 'CapacitorToast', :path => '..\..\node_modules\@capacitor\toast'
|
||||
pod 'HugotomaziCapacitorNavigationBar', :path => '..\..\node_modules\@hugotomazi\capacitor-navigation-bar'
|
||||
end
|
||||
|
||||
target 'App' do
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<p align="center">
|
||||
<a href="https://vuetube.app/">
|
||||
<img src="https://cdn.discordapp.com/attachments/751596360108605500/963045605071028274/vuetube-logo.svg" alt="VueTube icon" width="500"/>
|
||||
<img src="https://cdn.discordapp.com/attachments/751596360108605500/980418672331988992/VueTube_Dark.svg" alt="VueTube icon" width="500"/>
|
||||
</a>
|
||||
</br>
|
||||
<sub>Logo by <a href="https://github.com/afnzmn">@afnzmn</a></sub>
|
||||
|
|
Loading…
Reference in a new issue