Skip to content

Commit

Permalink
Extract Json parsing into a separate class
Browse files Browse the repository at this point in the history
And add tests so hopefully it will work better in the longrun.
  • Loading branch information
gzsombor committed Feb 10, 2025
1 parent 92ed180 commit 53a7c6c
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 83 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ dependencies {

// Convert Java objects into JSON and back
implementation 'com.google.code.gson:gson:2.10.1'
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"

// YouTube modules
implementation 'com.github.SkyTubeTeam.google-http-java-client:google-http-client-android:e726c5718d'
Expand Down Expand Up @@ -206,6 +207,7 @@ dependencies {
// Required for local unit tests (JUnit 5 framework)
def junitVersion = '5.10.2'
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.images.WebImage;
import com.google.gson.Gson;
import com.sothree.slidinguppanel.SlidingUpPanelLayout;

import org.schabi.newpipe.extractor.stream.StreamInfo;
Expand All @@ -60,6 +59,7 @@
import free.rm.skytube.app.SkyTubeApp;
import free.rm.skytube.app.StreamSelectionPolicy;
import free.rm.skytube.businessobjects.ChromecastListener;
import free.rm.skytube.businessobjects.JsonSerializer;
import free.rm.skytube.businessobjects.YouTube.POJOs.YouTubeVideo;
import free.rm.skytube.businessobjects.YouTube.YouTubeTasks;
import free.rm.skytube.businessobjects.YouTube.newpipe.ChannelId;
Expand Down Expand Up @@ -99,6 +99,7 @@ public abstract class BaseActivity extends AppCompatActivity implements MainActi
private Intent notificationClickIntent;

protected ActivityMainBinding binding;
protected final JsonSerializer jsonSerializer = new JsonSerializer();

private final CompositeDisposable compositeDisposable = new CompositeDisposable();

Expand Down Expand Up @@ -481,11 +482,10 @@ public void playVideoOnChromecast(final YouTubeVideo video, final int position)
public void onGetDesiredStream(StreamInfo desiredStream, YouTubeVideo video) {
if(mCastSession == null)
return;
Gson gson = new Gson();
final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_GENERIC);
metadata.putInt(KEY_POSITION, position);
metadata.putString(KEY_VIDEO, gson.toJson(video));
metadata.putString(KEY_VIDEO, jsonSerializer.toPersistedVideoJson(video));

metadata.addImage(new WebImage(Uri.parse(video.getThumbnailUrl())));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaStatus;
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import free.rm.skytube.businessobjects.JsonSerializer;
import free.rm.skytube.businessobjects.YouTube.POJOs.YouTubeVideo;
import free.rm.skytube.gui.activities.BaseActivity;
import free.rm.skytube.gui.businessobjects.MainActivityListener;
Expand All @@ -42,6 +41,7 @@ public abstract class ChromecastBaseControllerFragment extends FragmentEx {
protected boolean isSeeking = false;

protected YouTubeVideo video;
protected final JsonSerializer jsonSerializer = new JsonSerializer();

protected MainActivityListener mainActivityListener;

Expand Down Expand Up @@ -109,8 +109,7 @@ public void init(RemoteMediaClient client, MediaInfo media, int position) {
currentPlayingMedia = media;
currentPlayerState = remoteMediaClient.getPlayerState();

Gson gson = new Gson();
this.video = gson.fromJson(currentPlayingMedia.getMetadata().getString(BaseActivity.KEY_VIDEO), new TypeToken<YouTubeVideo>(){}.getType());
this.video = jsonSerializer.fromPersistedVideoJson(currentPlayingMedia.getMetadata().getString(BaseActivity.KEY_VIDEO));

if(currentPlayerState != MediaStatus.PLAYER_STATE_IDLE) {
updateButtons();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* SkyTube
* Copyright (C) 2025 Zsombor Gegesy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation (version 3 of the License).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package free.rm.skytube.businessobjects;

import com.google.gson.Gson;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;

import free.rm.skytube.businessobjects.Sponsorblock.SBVideoInfo;
import free.rm.skytube.businessobjects.YouTube.POJOs.YouTubeChannel;
import free.rm.skytube.businessobjects.YouTube.POJOs.YouTubeVideo;

public class JsonSerializer {
private final Gson gson = new Gson();

public YouTubeVideo fromPersistedVideoJson(String videoJson) {
if (videoJson == null) {
return null;
}
YouTubeVideo video = gson.fromJson(videoJson, YouTubeVideo.class);
video.updatePublishTimestampFromDate();

// due to upgrade to YouTubeVideo (by changing channel{Id,Name} to YouTubeChannel)
// from version 2.82 to 2.90
if (video.getChannel() == null) {
try {
JsonObject videoJsonObj = JsonParser.object().from(videoJson);
final String channelId = videoJsonObj.getString("channelId");
final String channelName = videoJsonObj.getString("channelName");
if (channelId != null && channelName != null) {
video.setChannel(new YouTubeChannel(channelId, channelName));
}
} catch (JsonParserException e) {
Logger.e(this, "Error occurred while extracting channel{Id,Name} from JSON", e);
}
}

return video;
}

public YouTubeVideo fromPersistedVideoJson(byte[] jsonVideo) {
if (jsonVideo != null) {
return fromPersistedVideoJson(new String(jsonVideo));
} else {
return null;
}
}

public String toPersistedVideoJson(YouTubeVideo video) {
return gson.toJson(video);
}

public String toPersistedSponsorBlockJson(SBVideoInfo sbVideoInfo) {
return gson.toJson(sbVideoInfo);
}

public SBVideoInfo fromSponsorBlockJson(String sponsorBlockJson) {
return gson.fromJson(sponsorBlockJson, SBVideoInfo.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ public class SBSegment {
private final double startPos;
private final double endPos;

public SBSegment(String category, double startPos, double endPos) {
this.category = category;
this.startPos = startPos;
this.endPos = endPos;
}

public SBSegment(JSONObject jsonSegment) throws JSONException {
category = jsonSegment.getString("category");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public class SBVideoInfo {
private final double videoDuration;
private final List<SBSegment> segments = new ArrayList<SBSegment>();

public SBVideoInfo(double videoDuration) {
this.videoDuration = videoDuration;
}

public SBVideoInfo(JSONArray sponsorblockInfo) throws JSONException {
double firstDuration = 0;
for (int i = 0; i < sponsorblockInfo.length(); i++) {
Expand All @@ -32,4 +36,12 @@ public double getVideoDuration() {
public List<SBSegment> getSegments() {
return segments;
}

@Override
public String toString() {
return "SBVideoInfo{" +
"videoDuration=" + videoDuration +
", segments=" + segments +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,10 @@ public class YouTubeVideo extends CardData implements Serializable {
* Video duration in seconds
*/
private int durationInSeconds = -1;
/**
* Total views count. This can be <b>null</b> if the video does not allow the user to
* like/dislike it. Format: "<number> Views"
*/
private String viewsCount;
/**
* Total views count.
*/
private BigInteger viewsCountInt;
private Long viewsCountInt;
/**
* The date/time of when this video was published.
*/
Expand Down Expand Up @@ -173,7 +168,7 @@ public YouTubeVideo(Video video) throws IllegalArgumentException {
if (statistics != null) {
setLikeDislikeCount(statistics.getLikeCount() != null ? statistics.getLikeCount().longValue() : null, statistics.getDislikeCount() != null ? statistics.getDislikeCount().longValue() : null);

setViewCount(statistics.getViewCount());
setViewCount(statistics.getViewCount().longValue());
}
}

Expand All @@ -185,9 +180,8 @@ public Integer getCategoryId() {
return categoryId;
}

public void setViewCount(BigInteger viewsCountInt) {
public void setViewCount(Long viewsCountInt) {
this.viewsCountInt = viewsCountInt;
this.viewsCount = String.format(getStr(R.string.views), viewsCountInt);
}

public YouTubeVideo(String id, String title, String description, long durationInSeconds,
Expand All @@ -198,7 +192,7 @@ public YouTubeVideo(String id, String title, String description, long durationIn
this.description = description;
setDurationInSeconds((int) durationInSeconds);
if (viewCount >= 0) {
setViewCount(BigInteger.valueOf(viewCount));
setViewCount(viewCount);
}
if (publishDate != null) {
setPublishTimestamp(publishDate.toEpochMilli());
Expand Down Expand Up @@ -374,10 +368,10 @@ private void setDuration(String duration) {
}

public String getViewsCount() {
return viewsCount;
return viewsCountInt != null ? String.format(getStr(R.string.views), viewsCountInt) : null;
}

public BigInteger getViewsCountInt() {
public Long getViewsCountInt() {
return viewsCountInt;
}

Expand Down Expand Up @@ -491,7 +485,7 @@ public void updateFromStreamInfo(StreamInfo streamInfo) {

final Long views = filterNegative(streamInfo.getViewCount());
if (views != null) {
this.setViewCount(BigInteger.valueOf(views));
this.setViewCount(views);
}
this.setDescription(NewPipeUtils.filterHtml(streamInfo.getDescription()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ private boolean filterByViews(YouTubeVideo video, BigInteger minimumVideoViews)
return false;

// if the video has less views than minimumVideoViews, then filter it out
if (video.getViewsCountInt().compareTo(minimumVideoViews) < 0) {
if (video.getViewsCountInt().longValue() < 0) {
log(video, FilterType.VIEWS, String.format(getStr(R.string.views), video.getViewsCountInt()));
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@
import androidx.core.util.Pair;

import com.github.skytube.components.utils.SQLiteHelper;
import com.google.gson.Gson;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashSet;
Expand All @@ -39,9 +35,9 @@

import free.rm.skytube.app.SkyTubeApp;
import free.rm.skytube.app.Utils;
import free.rm.skytube.businessobjects.JsonSerializer;
import free.rm.skytube.businessobjects.Logger;
import free.rm.skytube.businessobjects.YouTube.POJOs.CardData;
import free.rm.skytube.businessobjects.YouTube.POJOs.YouTubeChannel;
import free.rm.skytube.businessobjects.YouTube.POJOs.YouTubeVideo;
import free.rm.skytube.businessobjects.YouTube.newpipe.VideoId;
import free.rm.skytube.businessobjects.interfaces.OrderableDatabase;
Expand All @@ -57,6 +53,7 @@ public class BookmarksDb extends CardEventEmitterDatabase implements OrderableDa

private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "bookmarks.db";
private final JsonSerializer jsonSerializer = new JsonSerializer();

private BookmarksDb(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Expand Down Expand Up @@ -103,10 +100,9 @@ public Single<Boolean> isVideoBookmarked(@NonNull String videoId) {
* @return True if the video was successfully saved/bookmarked to the DB.
*/
private DatabaseResult add(YouTubeVideo video) {
Gson gson = new Gson();
ContentValues values = new ContentValues();
values.put(BookmarksTable.COL_YOUTUBE_VIDEO_ID, video.getId());
values.put(BookmarksTable.COL_YOUTUBE_VIDEO, gson.toJson(video).getBytes());
values.put(BookmarksTable.COL_YOUTUBE_VIDEO, jsonSerializer.toPersistedVideoJson(video).getBytes());

int order = getMaximumOrderNumber();
order++;
Expand Down Expand Up @@ -173,10 +169,9 @@ private DatabaseResult remove(VideoId video) {
null,
null, null, null, BookmarksTable.COL_ORDER + " ASC")) {
int blobCol = cursor.getColumnIndexOrThrow(BookmarksTable.COL_YOUTUBE_VIDEO);
Gson gson = new Gson();
while (cursor.moveToNext()) {
byte[] blob = cursor.getBlob(blobCol);
YouTubeVideo uvideo = gson.fromJson(new String(blob), YouTubeVideo.class).updatePublishTimestampFromDate();
YouTubeVideo uvideo = jsonSerializer.fromPersistedVideoJson(blob).updatePublishTimestampFromDate();
ContentValues contentValues = new ContentValues();
contentValues.put(BookmarksTable.COL_ORDER, order++);

Expand Down Expand Up @@ -261,7 +256,6 @@ public int getMaximumOrderNumber() {

List<YouTubeVideo> videos = new ArrayList<>();

final Gson gson = new Gson();
Integer minOrder = null;
if(cursor.moveToNext()) {
final int colOrder = cursor.getColumnIndex(BookmarksTable.COL_ORDER);
Expand All @@ -272,28 +266,9 @@ public int getMaximumOrderNumber() {

minOrder = Utils.min(currentOrder, minOrder);

final String videoJson = new String(blob);

// convert JSON into YouTubeVideo
YouTubeVideo video = gson.fromJson(videoJson, YouTubeVideo.class).updatePublishTimestampFromDate();

// Logger.i(this, "Order "+cursor.getInt(colOrder)+ ", id="+video.getId()+","+video.getTitle());

// due to upgrade to YouTubeVideo (by changing channel{Id,Name} to YouTubeChannel)
// from version 2.82 to 2.90
if (video.getChannel() == null) {
try {
JSONObject videoJsonObj = new JSONObject(videoJson);
final String channelId = videoJsonObj.get("channelId").toString();
final String channelName = videoJsonObj.get("channelName").toString();
video.setChannel(new YouTubeChannel(channelId, channelName));
} catch (JSONException e) {
Logger.e(this, "Error occurred while extracting channel{Id,Name} from JSON", e);
}
}

// regenerate the video's PublishDatePretty (e.g. 5 hours ago)
//video.forceRefreshPublishDatePretty();
YouTubeVideo video = jsonSerializer.fromPersistedVideoJson(blob);

// add the video to the list
videos.add(video);
} while(cursor.moveToNext());
Expand Down Expand Up @@ -321,5 +296,4 @@ public int getMaximumOrderNumber() {
return results;
}


}
Loading

0 comments on commit 53a7c6c

Please sign in to comment.