@@ -7,8 +7,8 @@ android {
|
||||
applicationId "us.materialio.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 44
|
||||
versionName "1.5.3"
|
||||
versionCode 45
|
||||
versionName "1.5.4"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -12,6 +12,7 @@ dependencies {
|
||||
implementation project(':capacitor-app')
|
||||
implementation project(':capacitor-browser')
|
||||
implementation project(':capgo-inappbrowser')
|
||||
implementation project(':mediagrid-capacitor-native-audio')
|
||||
implementation project(':capacitor-nodejs')
|
||||
|
||||
}
|
||||
|
||||
@@ -34,9 +34,23 @@
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<service
|
||||
android:name="us.mediagrid.capacitorjs.plugins.nativeaudio.AudioPlayerService"
|
||||
android:description="@string/audio_player_service_description"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaSessionService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
<!-- Permissions -->
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
</manifest>
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
<string name="title_activity_main">Materialious</string>
|
||||
<string name="package_name">us.materialio.app</string>
|
||||
<string name="custom_url_scheme">us.materialio.app</string>
|
||||
<string name="audio_player_service_description">Allows for audio to play in the background.</string>
|
||||
</resources>
|
||||
|
||||
@@ -11,5 +11,8 @@ project(':capacitor-browser').projectDir = new File('../node_modules/@capacitor/
|
||||
include ':capgo-inappbrowser'
|
||||
project(':capgo-inappbrowser').projectDir = new File('../node_modules/@capgo/inappbrowser/android')
|
||||
|
||||
include ':mediagrid-capacitor-native-audio'
|
||||
project(':mediagrid-capacitor-native-audio').projectDir = new File('../node_modules/@mediagrid/capacitor-native-audio/android')
|
||||
|
||||
include ':capacitor-nodejs'
|
||||
project(':capacitor-nodejs').projectDir = new File('../node_modules/capacitor-nodejs/android')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.4",
|
||||
"description": "Modern material design for Invidious.",
|
||||
"author": {
|
||||
"name": "Ward Pearce",
|
||||
|
||||
Generated
+11
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "materialious",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.4",
|
||||
"dependencies": {
|
||||
"@capacitor-community/electron": "^5.0.1",
|
||||
"@capacitor/android": "6.1.2",
|
||||
@@ -16,6 +16,7 @@
|
||||
"@capacitor/core": "^6.1.2",
|
||||
"@capgo/inappbrowser": "^6.6.8",
|
||||
"@ffmpeg/ffmpeg": "^0.12.10",
|
||||
"@mediagrid/capacitor-native-audio": "^1.0.0",
|
||||
"beercss": "^3.7.7",
|
||||
"capacitor-nodejs": "https://github.com/EdenwareApps/Capacitor-NodeJS/releases/download/v1.0.0-beta.7/capacitor6-nodejs.tgz",
|
||||
"dashjs": "^4.7.4",
|
||||
@@ -2725,6 +2726,14 @@
|
||||
"resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz",
|
||||
"integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ=="
|
||||
},
|
||||
"node_modules/@mediagrid/capacitor-native-audio": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@mediagrid/capacitor-native-audio/-/capacitor-native-audio-1.0.0.tgz",
|
||||
"integrity": "sha512-jYTKFqMnVxSwlRUm8pO1jGQgoF1dY00xFGJZXn1AUkSI7BLE+kEpVUYjsGn1CuMH5me9aEyOtiVaif5zF2Mwxw==",
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@msgpack/msgpack": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
@@ -52,6 +52,7 @@
|
||||
"@capacitor/core": "^6.1.2",
|
||||
"@capgo/inappbrowser": "^6.6.8",
|
||||
"@ffmpeg/ffmpeg": "^0.12.10",
|
||||
"@mediagrid/capacitor-native-audio": "^1.0.0",
|
||||
"beercss": "^3.7.7",
|
||||
"capacitor-nodejs": "https://github.com/EdenwareApps/Capacitor-NodeJS/releases/download/v1.0.0-beta.7/capacitor6-nodejs.tgz",
|
||||
"dashjs": "^4.7.4",
|
||||
@@ -71,4 +72,4 @@
|
||||
"terser": "^5.33.0",
|
||||
"vidstack": "^1.12.9"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
deArrowThumbnailInstanceStore,
|
||||
instanceStore,
|
||||
interfaceRegionStore,
|
||||
playerYouTubeJsAlways,
|
||||
playerYouTubeJsFallback,
|
||||
returnYTDislikesInstanceStore,
|
||||
synciousInstanceStore
|
||||
@@ -72,7 +73,9 @@ export async function getPopular(fetchOptions?: RequestInit): Promise<Video[]> {
|
||||
|
||||
export async function getVideo(videoId: string, local: boolean = false, fetchOptions?: RequestInit): Promise<VideoPlay> {
|
||||
const resp = await fetch(setRegion(buildPath(`videos/${videoId}?local=${local}`)), fetchOptions);
|
||||
if (!resp.ok && Capacitor.isNativePlatform() && get(playerYouTubeJsFallback)) {
|
||||
if (
|
||||
(get(playerYouTubeJsAlways) && Capacitor.isNativePlatform()) ||
|
||||
(!resp.ok && get(playerYouTubeJsFallback) && Capacitor.isNativePlatform())) {
|
||||
return await patchYoutubeJs(videoId);
|
||||
} else {
|
||||
await fetchErrorHandle(resp);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import { page } from '$app/stores';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { AudioPlayer } from '@mediagrid/capacitor-native-audio';
|
||||
import type { Page } from '@sveltejs/kit';
|
||||
import { SponsorBlock, type Category, type Segment } from 'sponsorblock-api';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
@@ -14,6 +15,7 @@
|
||||
import type { VideoPlay } from './Api/model';
|
||||
import {
|
||||
getBestThumbnail,
|
||||
proxyVideoUrl,
|
||||
pullBitratePreference,
|
||||
videoLength,
|
||||
type PhasedDescription
|
||||
@@ -23,6 +25,7 @@
|
||||
instanceStore,
|
||||
miniPlayerSrcStore,
|
||||
playerAlwaysLoopStore,
|
||||
playerAndroidBgPlayer,
|
||||
playerAutoPlayStore,
|
||||
playerProxyVideosStore,
|
||||
playerSavePlaybackPositionStore,
|
||||
@@ -326,7 +329,7 @@
|
||||
src = [{ src: data.video.dashUrl, type: 'application/dash+xml' }];
|
||||
|
||||
if (!data.video.fallbackPatch) {
|
||||
if (Capacitor.getPlatform() !== 'electron' || proxyVideos) {
|
||||
if (!Capacitor.isNativePlatform() || proxyVideos) {
|
||||
(src[0] as { src: string }).src += '?local=true';
|
||||
}
|
||||
}
|
||||
@@ -337,6 +340,46 @@
|
||||
}
|
||||
await loadPlayerPos();
|
||||
});
|
||||
|
||||
if (Capacitor.getPlatform() === 'android' && get(playerAndroidBgPlayer)) {
|
||||
const highestBitrateAudio = data.video.adaptiveFormats
|
||||
.filter((format) => format.type.startsWith('audio/'))
|
||||
.reduce((prev, current) => {
|
||||
return parseInt(prev.bitrate) > parseInt(current.bitrate) ? prev : current;
|
||||
});
|
||||
|
||||
const audioId = { audioId: data.video.videoId };
|
||||
|
||||
await AudioPlayer.create({
|
||||
...audioId,
|
||||
audioSource: proxyVideoUrl(highestBitrateAudio.url),
|
||||
friendlyTitle: data.video.title,
|
||||
useForNotification: true,
|
||||
loop: player.loop,
|
||||
isBackgroundMusic: false
|
||||
});
|
||||
|
||||
AudioPlayer.onAppGainsFocus(audioId, async () => {
|
||||
await AudioPlayer.pause(audioId);
|
||||
|
||||
const audioPlayerTime = await AudioPlayer.getCurrentTime(audioId);
|
||||
|
||||
player.currentTime = Math.round(audioPlayerTime.currentTime);
|
||||
await player.play();
|
||||
});
|
||||
|
||||
AudioPlayer.onAppLosesFocus(audioId, async () => {
|
||||
if (!player.paused) {
|
||||
await AudioPlayer.play(audioId);
|
||||
await AudioPlayer.seek({
|
||||
...audioId,
|
||||
timeInSeconds: Math.round(player.currentTime)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await AudioPlayer.initialize(audioId);
|
||||
}
|
||||
} else {
|
||||
playerIsLive = true;
|
||||
src = [
|
||||
@@ -434,12 +477,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
onDestroy(async () => {
|
||||
if (Capacitor.getPlatform() === 'android') {
|
||||
await AudioPlayer.destroy({ audioId: data.video.videoId });
|
||||
}
|
||||
if (typeof silenceSkipperInterval !== 'undefined') {
|
||||
clearInterval(silenceSkipperInterval);
|
||||
}
|
||||
savePlayerPos();
|
||||
player.pause();
|
||||
await player.pause();
|
||||
player.destroy();
|
||||
playerPosSet = false;
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
interfaceRegionStore,
|
||||
interfaceSearchSuggestionsStore,
|
||||
playerAlwaysLoopStore,
|
||||
playerAndroidBgPlayer,
|
||||
playerAutoPlayStore,
|
||||
playerAutoplayNextByDefaultStore,
|
||||
playerListenByDefaultStore,
|
||||
@@ -29,6 +30,7 @@
|
||||
playerProxyVideosStore,
|
||||
playerSavePlaybackPositionStore,
|
||||
playerTheatreModeByDefaultStore,
|
||||
playerYouTubeJsAlways,
|
||||
playerYouTubeJsFallback,
|
||||
returnYTDislikesInstanceStore,
|
||||
returnYtDislikesStore,
|
||||
@@ -306,6 +308,24 @@
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{#if Capacitor.getPlatform() === 'android'}
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
<div>{$_('layout.player.backgroundPlay')}</div>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={$playerAndroidBgPlayer}
|
||||
on:click={() => playerAndroidBgPlayer.set(!$playerAndroidBgPlayer)}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if Capacitor.isNativePlatform()}
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
@@ -406,6 +426,22 @@
|
||||
</div>
|
||||
|
||||
{#if Capacitor.isNativePlatform()}
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
<div>{$_('layout.player.youtubeJsAlways')}</div>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={$playerYouTubeJsAlways}
|
||||
on:click={() => playerYouTubeJsAlways.set(!$playerYouTubeJsAlways)}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
|
||||
@@ -125,7 +125,8 @@
|
||||
"backgroundPlay": "Play audio in background",
|
||||
"dash": "Dash",
|
||||
"silenceSkipper": "Silence skipper (Experimental)",
|
||||
"youtubeJsFallback": "Local video processing fallback"
|
||||
"youtubeJsFallback": "Local video processing fallback",
|
||||
"youtubeJsAlways": "Always use local video processing"
|
||||
},
|
||||
"bookmarklet": "Bookmarklet",
|
||||
"instanceUrl": "Instance URL",
|
||||
|
||||
@@ -14,7 +14,7 @@ if (Capacitor.getPlatform() === 'android') {
|
||||
typeof requestInput === 'string' ? requestInput : requestInput.toString();
|
||||
|
||||
// Check if the URL is already proxied, to avoid double proxying
|
||||
if (!requestUrl.startsWith(corsAnywhereProxyUrl) && !requestUrl.startsWith('/')) {
|
||||
if (!requestUrl.startsWith(corsAnywhereProxyUrl) && !requestUrl.startsWith('/') && !requestUrl.startsWith('blob:') && !requestUrl.startsWith('/')) {
|
||||
requestInput = corsAnywhereProxyUrl + requestUrl;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ export const playerTheatreModeByDefaultStore = persisted('theatreModeByDefault',
|
||||
export const playerAutoplayNextByDefaultStore = persisted('autoplayNextByDefault', false);
|
||||
export const playerMiniPlayerStore = persisted('miniPlayer', true);
|
||||
export const playerYouTubeJsFallback = persisted('youTubeJsFallback', true);
|
||||
export const playerYouTubeJsAlways = persisted('youTubeJsAlways', false);
|
||||
export const playerAndroidBgPlayer = persisted('androidBgPlayer', true);
|
||||
|
||||
export const returnYtDislikesStore = persisted('returnYtDislikes', false);
|
||||
export const returnYTDislikesInstanceStore: Writable<string | null | undefined> = persisted(
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
|
||||
LATEST_VERSION = "1.5.3"
|
||||
LATEST_VERSION = "1.5.4"
|
||||
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")
|
||||
|
||||
ROOT_PACKAGE = os.path.join(WORKING_DIR, "package.json")
|
||||
|
||||
Reference in New Issue
Block a user